changeset 82:5b127ab0080b

sync
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 14 Dec 2016 17:01:34 +0100
parents d101055fc20b
children 0cb3ac4f9159
files Applications/CMakeLists.txt Resources/Orthanc/Core/DicomFormat/DicomArray.h Resources/Orthanc/Core/DicomFormat/DicomMap.h Resources/Orthanc/Core/DicomFormat/DicomTag.h Resources/Orthanc/Core/Enumerations.cpp Resources/Orthanc/Core/HttpClient.cpp Resources/Orthanc/Core/Images/JpegWriter.cpp Resources/Orthanc/Core/MultiThreading/BagOfTasksProcessor.h Resources/Orthanc/Core/MultiThreading/Semaphore.h Resources/Orthanc/Core/OrthancException.h Resources/Orthanc/OrthancServer/FromDcmtkBridge.cpp Resources/Orthanc/OrthancServer/FromDcmtkBridge.h Resources/Orthanc/OrthancServer/ServerEnumerations.cpp Resources/Orthanc/OrthancServer/ServerEnumerations.h Resources/Orthanc/OrthancServer/ToDcmtkBridge.cpp Resources/Orthanc/OrthancServer/ToDcmtkBridge.h Resources/Orthanc/Plugins/Engine/SharedLibrary.h Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h Resources/Orthanc/Resources/CMake/DcmtkConfiguration.cmake
diffstat 20 files changed, 365 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/CMakeLists.txt	Tue Nov 29 13:31:23 2016 +0100
+++ b/Applications/CMakeLists.txt	Wed Dec 14 17:01:34 2016 +0100
@@ -78,14 +78,16 @@
 include(${ORTHANC_WSI_DIR}/Resources/CMake/LibTiffConfiguration.cmake)
 
 add_definitions(
+  -DORTHANC_BUILD_UNIT_TESTS=0  # For FromDcmtkBridge
   -DORTHANC_ENABLE_BASE64=1
   -DORTHANC_ENABLE_CURL=1
   -DORTHANC_ENABLE_DCMTK=1
+  -DORTHANC_ENABLE_JPEG=0       # Disable DCMTK's support for JPEG
   -DORTHANC_ENABLE_LOGGING=1
+  -DORTHANC_ENABLE_LUA=0        # For FromDcmtkBridge
   -DORTHANC_ENABLE_MD5=0
-  -DORTHANC_ENABLE_JPEG=0     # Disable DCMTK's support for JPEG
   -DORTHANC_ENABLE_PKCS11=0
-  -DORTHANC_ENABLE_PLUGINS=1  # To enable class Orthanc::SharedLibrary
+  -DORTHANC_ENABLE_PLUGINS=1    # To enable class Orthanc::SharedLibrary
   -DORTHANC_ENABLE_PUGIXML=0
   -DORTHANC_SANDBOXED=0
   -DHAS_ORTHANC_EXCEPTION=1
--- a/Resources/Orthanc/Core/DicomFormat/DicomArray.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Core/DicomFormat/DicomArray.h	Wed Dec 14 17:01:34 2016 +0100
@@ -47,7 +47,7 @@
     Elements  elements_;
 
   public:
-    DicomArray(const DicomMap& map);
+    explicit DicomArray(const DicomMap& map);
 
     ~DicomArray();
 
--- a/Resources/Orthanc/Core/DicomFormat/DicomMap.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Core/DicomFormat/DicomMap.h	Wed Dec 14 17:01:34 2016 +0100
@@ -47,7 +47,7 @@
   private:
     friend class DicomArray;
     friend class FromDcmtkBridge;
-    friend class ToDcmtkBridge;
+    friend class ParsedDicomFile;
 
     typedef std::map<DicomTag, DicomValue*>  Map;
 
--- a/Resources/Orthanc/Core/DicomFormat/DicomTag.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Core/DicomFormat/DicomTag.h	Wed Dec 14 17:01:34 2016 +0100
@@ -100,6 +100,7 @@
   static const DicomTag DICOM_TAG_SERIES_INSTANCE_UID(0x0020, 0x000e);
   static const DicomTag DICOM_TAG_STUDY_INSTANCE_UID(0x0020, 0x000d);
   static const DicomTag DICOM_TAG_PIXEL_DATA(0x7fe0, 0x0010);
+  static const DicomTag DICOM_TAG_TRANSFER_SYNTAX_UID(0x0002, 0x0010);
 
   static const DicomTag DICOM_TAG_IMAGE_INDEX(0x0054, 0x1330);
   static const DicomTag DICOM_TAG_INSTANCE_NUMBER(0x0020, 0x0013);
@@ -127,6 +128,7 @@
   static const DicomTag DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER(0x0020, 0x0100);
 
   // Tags for C-FIND and C-MOVE
+  static const DicomTag DICOM_TAG_MESSAGE_ID(0x0000, 0x0110);
   static const DicomTag DICOM_TAG_SPECIFIC_CHARACTER_SET(0x0008, 0x0005);
   static const DicomTag DICOM_TAG_QUERY_RETRIEVE_LEVEL(0x0008, 0x0052);
   static const DicomTag DICOM_TAG_MODALITIES_IN_STUDY(0x0008, 0x0061);
--- a/Resources/Orthanc/Core/Enumerations.cpp	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Core/Enumerations.cpp	Wed Dec 14 17:01:34 2016 +0100
@@ -1083,15 +1083,18 @@
   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" ||
@@ -1238,8 +1241,10 @@
     // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
     switch (encoding)
     {
+      case Encoding_Ascii:
+        return "ISO_IR 6";
+
       case Encoding_Utf8:
-      case Encoding_Ascii:
         return "ISO_IR 192";
 
       case Encoding_Latin1:
--- a/Resources/Orthanc/Core/HttpClient.cpp	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Core/HttpClient.cpp	Wed Dec 14 17:01:34 2016 +0100
@@ -318,10 +318,10 @@
     url_ = "";
     method_ = HttpMethod_Get;
     lastStatus_ = HttpStatus_200_Ok;
-    isVerbose_ = false;
+    SetVerbose(false);
     timeout_ = GlobalParameters::GetInstance().GetDefaultTimeout();
     GlobalParameters::GetInstance().GetDefaultProxy(proxy_);
-    GlobalParameters::GetInstance().GetSslConfiguration(verifyPeers_, caCertificates_);
+    GlobalParameters::GetInstance().GetSslConfiguration(verifyPeers_, caCertificates_);    
   }
 
 
--- a/Resources/Orthanc/Core/Images/JpegWriter.cpp	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Core/Images/JpegWriter.cpp	Wed Dec 14 17:01:34 2016 +0100
@@ -105,7 +105,7 @@
 
   void JpegWriter::SetQuality(uint8_t quality)
   {
-    if (quality <= 0 || quality > 100)
+    if (quality == 0 || quality > 100)
     {
       throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
--- a/Resources/Orthanc/Core/MultiThreading/BagOfTasksProcessor.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Core/MultiThreading/BagOfTasksProcessor.h	Wed Dec 14 17:01:34 2016 +0100
@@ -64,10 +64,10 @@
       {
       }
 
-      Bag(size_t size) : 
-      size_(size),
-      done_(0),
-      status_(BagStatus_Running)
+      explicit Bag(size_t size) : 
+        size_(size),
+        done_(0),
+        status_(BagStatus_Running)
       {
       }
     };
@@ -140,7 +140,7 @@
     };
   
 
-    BagOfTasksProcessor(size_t countThreads);
+    explicit BagOfTasksProcessor(size_t countThreads);
 
     ~BagOfTasksProcessor();
 
--- a/Resources/Orthanc/Core/MultiThreading/Semaphore.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Core/MultiThreading/Semaphore.h	Wed Dec 14 17:01:34 2016 +0100
@@ -57,7 +57,7 @@
       Semaphore&  that_;
 
     public:
-      Locker(Semaphore& that) :
+      explicit Locker(Semaphore& that) :
         that_(that)
       {
         that_.Acquire();
--- a/Resources/Orthanc/Core/OrthancException.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Core/OrthancException.h	Wed Dec 14 17:01:34 2016 +0100
@@ -45,7 +45,7 @@
     HttpStatus httpStatus_;
 
   public:
-    OrthancException(ErrorCode errorCode) : 
+    explicit OrthancException(ErrorCode errorCode) : 
       errorCode_(errorCode),
       httpStatus_(ConvertErrorCodeToHttpStatus(errorCode))
     {
--- a/Resources/Orthanc/OrthancServer/FromDcmtkBridge.cpp	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/OrthancServer/FromDcmtkBridge.cpp	Wed Dec 14 17:01:34 2016 +0100
@@ -1646,7 +1646,9 @@
 
     if (!ok)
     {
-      throw OrthancException(ErrorCode_InternalError);
+      LOG(ERROR) << "While creating a DICOM instance, tag (" << tag.Format()
+                 << ") has out-of-range value: \"" << *decoded << "\"";
+      throw OrthancException(ErrorCode_BadFileFormat);
     }
   }
 
@@ -1665,6 +1667,11 @@
         FillElementWithString(*element, tag, value.asString(), decodeDataUriScheme, dicomEncoding);
         break;
 
+      case Json::nullValue:
+        element.reset(CreateElementForTag(tag));
+        FillElementWithString(*element, tag, "", decodeDataUriScheme, dicomEncoding);
+        break;
+
       case Json::arrayValue:
       {
         DcmTag key(tag.GetGroup(), tag.GetElement());
@@ -1742,11 +1749,17 @@
       {
         const Json::Value& value = json[tags[i]];
         if (value.type() != Json::stringValue ||
-            !GetDicomEncoding(encoding, value.asCString()))
+            (value.asString().length() != 0 &&
+             !GetDicomEncoding(encoding, value.asCString())))
         {
           LOG(ERROR) << "Unknown encoding while creating DICOM from JSON: " << value;
           throw OrthancException(ErrorCode_BadRequest);
         }
+
+        if (value.asString().length() == 0)
+        {
+          return defaultEncoding;
+        }
       }
     }
 
@@ -1897,4 +1910,108 @@
       target.SetValue(ParseTag(members[i]), value.asString(), false);
     }
   }
+
+
+  void FromDcmtkBridge::ChangeStringEncoding(DcmItem& dataset,
+                                             Encoding source,
+                                             Encoding target)
+  {
+    // Recursive exploration of a dataset to change the encoding of
+    // each string-like element
+
+    if (source == target)
+    {
+      return;
+    }
+
+    for (unsigned long i = 0; i < dataset.card(); i++)
+    {
+      DcmElement* element = dataset.getElement(i);
+      if (element)
+      {
+        if (element->isLeaf())
+        {
+          char *c = NULL;
+          if (element->isaString() &&
+              element->getString(c).good() && 
+              c != NULL)
+          {
+            std::string a = Toolbox::ConvertToUtf8(c, source);
+            std::string b = Toolbox::ConvertFromUtf8(a, target);
+            element->putString(b.c_str());
+          }
+        }
+        else
+        {
+          // "All subclasses of DcmElement except for DcmSequenceOfItems
+          // are leaf nodes, while DcmSequenceOfItems, DcmItem, DcmDataset
+          // etc. are not." The following dynamic_cast is thus OK.
+          DcmSequenceOfItems& sequence = dynamic_cast<DcmSequenceOfItems&>(*element);
+
+          for (unsigned long j = 0; j < sequence.card(); j++)
+          {
+            ChangeStringEncoding(*sequence.getItem(j), source, target);
+          }
+        }
+      }
+    }
+  }
+
+
+  bool FromDcmtkBridge::LookupTransferSyntax(std::string& result,
+                                             DcmFileFormat& dicom)
+  {
+    const char* value = NULL;
+
+    if (dicom.getMetaInfo() != NULL &&
+        dicom.getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, value).good() &&
+        value != NULL)
+    {
+      result.assign(value);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+#if ORTHANC_ENABLE_LUA == 1
+  void FromDcmtkBridge::ExecuteToDicom(DicomMap& target,
+                                       LuaFunctionCall& call)
+  {
+    Json::Value output;
+    call.ExecuteToJson(output, true /* keep strings */);
+
+    target.Clear();
+
+    if (output.type() == Json::arrayValue &&
+        output.size() == 0)
+    {
+      // This case happens for empty tables
+      return;
+    }
+
+    if (output.type() != Json::objectValue)
+    {
+      LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table";
+      throw OrthancException(ErrorCode_LuaBadOutput);
+    }
+
+    Json::Value::Members members = output.getMemberNames();
+
+    for (size_t i = 0; i < members.size(); i++)
+    {
+      if (output[members[i]].type() != Json::stringValue)
+      {
+        LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table mapping names of DICOM tags to strings";
+        throw OrthancException(ErrorCode_LuaBadOutput);
+      }
+
+      DicomTag tag(ParseTag(members[i]));
+      target.SetValue(tag, output[members[i]].asString(), false);
+    }
+  }
+#endif
 }
--- a/Resources/Orthanc/OrthancServer/FromDcmtkBridge.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/OrthancServer/FromDcmtkBridge.h	Wed Dec 14 17:01:34 2016 +0100
@@ -43,10 +43,22 @@
 #include <dcmtk/dcmdata/dcfilefo.h>
 #include <json/json.h>
 
+#if !defined(ORTHANC_BUILD_UNIT_TESTS)
+#  error The macro ORTHANC_BUILD_UNIT_TESTS must be defined
+#endif
+
+#if !defined(ORTHANC_ENABLE_LUA)
+#  error The macro ORTHANC_ENABLE_LUA must be defined
+#endif
+
 #if ORTHANC_BUILD_UNIT_TESTS == 1
 #  include <gtest/gtest_prod.h>
 #endif
 
+#if ORTHANC_ENABLE_LUA == 1
+#  include "../Core/Lua/LuaFunctionCall.h"
+#endif
+
 
 namespace Orthanc
 {
@@ -88,6 +100,10 @@
                                    unsigned int maxStringLength,
                                    Encoding defaultEncoding);
 
+    static void ChangeStringEncoding(DcmItem& dataset,
+                                     Encoding source,
+                                     Encoding target);
+
   public:
     static void InitializeDictionary(bool loadPrivateDictionary);
 
@@ -195,5 +211,13 @@
 
     static void FromJson(DicomMap& values,
                          const Json::Value& result);
+
+    static bool LookupTransferSyntax(std::string& result,
+                                     DcmFileFormat& dicom);
+
+#if ORTHANC_ENABLE_LUA == 1
+    static void ExecuteToDicom(DicomMap& target,
+                               LuaFunctionCall& call);
+#endif
   };
 }
--- a/Resources/Orthanc/OrthancServer/ServerEnumerations.cpp	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/OrthancServer/ServerEnumerations.cpp	Wed Dec 14 17:01:34 2016 +0100
@@ -64,6 +64,8 @@
     dictMetadataType_.Add(MetadataType_AnonymizedFrom, "AnonymizedFrom");
     dictMetadataType_.Add(MetadataType_LastUpdate, "LastUpdate");
     dictMetadataType_.Add(MetadataType_Instance_Origin, "Origin");
+    dictMetadataType_.Add(MetadataType_Instance_TransferSyntax, "TransferSyntax");
+    dictMetadataType_.Add(MetadataType_Instance_SopClassUid, "SopClassUid");
 
     dictContentType_.Add(FileContentType_Dicom, "dicom");
     dictContentType_.Add(FileContentType_DicomAsJson, "dicom-as-json");
--- a/Resources/Orthanc/OrthancServer/ServerEnumerations.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/OrthancServer/ServerEnumerations.h	Wed Dec 14 17:01:34 2016 +0100
@@ -157,7 +157,9 @@
     MetadataType_ModifiedFrom = 5,
     MetadataType_AnonymizedFrom = 6,
     MetadataType_LastUpdate = 7,
-    MetadataType_Instance_Origin = 8,   // New in Orthanc 0.9.5
+    MetadataType_Instance_Origin = 8,          // New in Orthanc 0.9.5
+    MetadataType_Instance_TransferSyntax = 9,  // New in Orthanc 1.2.0
+    MetadataType_Instance_SopClassUid = 10,    // New in Orthanc 1.2.0
 
     // Make sure that the value "65535" can be stored into this enumeration
     MetadataType_StartUser = 1024,
--- a/Resources/Orthanc/OrthancServer/ToDcmtkBridge.cpp	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/OrthancServer/ToDcmtkBridge.cpp	Wed Dec 14 17:01:34 2016 +0100
@@ -41,24 +41,6 @@
 
 namespace Orthanc
 {
-  DcmDataset* ToDcmtkBridge::Convert(const DicomMap& map)
-  {
-    std::auto_ptr<DcmDataset> result(new DcmDataset);
-
-    for (DicomMap::Map::const_iterator 
-           it = map.map_.begin(); it != map.map_.end(); ++it)
-    {
-      if (!it->second->IsNull())
-      {
-        std::string s = it->second->GetContent();
-        DU_putStringDOElement(result.get(), Convert(it->first), s.c_str());
-      }
-    }
-
-    return result.release();
-  }
-
-
   DcmEVR ToDcmtkBridge::Convert(ValueRepresentation vr)
   {
     switch (vr)
--- a/Resources/Orthanc/OrthancServer/ToDcmtkBridge.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/OrthancServer/ToDcmtkBridge.h	Wed Dec 14 17:01:34 2016 +0100
@@ -45,8 +45,6 @@
       return DcmTagKey(tag.GetGroup(), tag.GetElement());
     }
 
-    static DcmDataset* Convert(const DicomMap& map);
-
     static DcmEVR Convert(ValueRepresentation vr);
   };
 }
--- a/Resources/Orthanc/Plugins/Engine/SharedLibrary.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Plugins/Engine/SharedLibrary.h	Wed Dec 14 17:01:34 2016 +0100
@@ -60,7 +60,7 @@
     FunctionPointer GetFunctionInternal(const std::string& name);
 
   public:
-    SharedLibrary(const std::string& path);
+    explicit SharedLibrary(const std::string& path);
 
     ~SharedLibrary();
 
--- a/Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Wed Dec 14 17:01:34 2016 +0100
@@ -276,11 +276,24 @@
   }
 
 
-  OrthancString::OrthancString(OrthancPluginContext* context,
-                               char* str) :
-    context_(context),
-    str_(str)
+  void MemoryBuffer::GetDicomQuery(const OrthancPluginWorklistQuery* query)
+  {
+    Clear();
+    Check(OrthancPluginWorklistGetDicomQuery(context_, &buffer_, query));
+  }
+
+
+  void OrthancString::Assign(char* str)
   {
+    if (str == NULL)
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(OrthancPluginErrorCode_InternalError);
+    }
+    else
+    {
+      Clear();
+      str_ = str;
+    }
   }
 
 
@@ -322,12 +335,25 @@
       ORTHANC_PLUGINS_THROW_EXCEPTION(OrthancPluginErrorCode_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);
+  }
+
   
 
   OrthancConfiguration::OrthancConfiguration(OrthancPluginContext* context) : 
     context_(context)
   {
-    OrthancString str(context, OrthancPluginGetConfiguration(context));
+    OrthancString str(context);
+    str.Assign(OrthancPluginGetConfiguration(context));
 
     if (str.GetContent() == NULL)
     {
@@ -809,6 +835,83 @@
   }
 
 
+
+#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(OrthancPluginErrorCode_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(OrthancPluginErrorCode_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(OrthancPluginErrorCode_InternalError);
+    }
+
+    if (result == 0)
+    {
+      return false;
+    }
+    else if (result == 1)
+    {
+      return true;
+    }
+    else
+    {
+      ORTHANC_PLUGINS_THROW_EXCEPTION(OrthancPluginErrorCode_InternalError);
+    }
+  }
+
+#endif /* HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1 */
+
+
   bool RestApiGet(Json::Value& result,
                   OrthancPluginContext* context,
                   const std::string& uri,
--- a/Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Wed Dec 14 17:01:34 2016 +0100
@@ -50,6 +50,17 @@
 #endif
 
 
+#if (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER >= 2 ||   \
+     (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER == 1 &&  \
+      ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER >= 2))
+// 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
 {
@@ -172,6 +183,13 @@
                      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);
   };
 
 
@@ -181,16 +199,23 @@
     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
     {
@@ -262,7 +287,7 @@
                         float defaultValue) const;
   };
 
-  class OrthancImage
+  class OrthancImage : public boost::noncopyable
   {
   private:
     OrthancPluginContext*  context_;
@@ -320,6 +345,48 @@
   };
 
 
+#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,
--- a/Resources/Orthanc/Resources/CMake/DcmtkConfiguration.cmake	Tue Nov 29 13:31:23 2016 +0100
+++ b/Resources/Orthanc/Resources/CMake/DcmtkConfiguration.cmake	Wed Dec 14 17:01:34 2016 +0100
@@ -50,6 +50,17 @@
     else()
       message("Using the dictionary of private tags from DCMTK 3.6.0")
     endif()
+
+    # Patches specific to DCMTK 3.6.0
+    execute_process(
+      COMMAND ${PATCH_EXECUTABLE} -p0 -N -i ${ORTHANC_ROOT}/Resources/Patches/dcmtk-3.6.0-dulparse-vulnerability.patch
+      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+      RESULT_VARIABLE Failure
+      )
+
+    if (Failure)
+      message(FATAL_ERROR "Error while patching a file")
+    endif()
   endif()