changeset 134:4d2a2e39c25a dev

rename
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 24 Jun 2016 12:01:33 +0200
parents a6c099292e55
children 36be09e49ad1
files CMakeLists.txt Plugin/DicomWebClient.cpp Plugin/DicomWebClient.h Plugin/Plugin.cpp Plugin/StowRsClient.cpp Plugin/StowRsClient.h
diffstat 6 files changed, 333 insertions(+), 333 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Jun 24 11:56:33 2016 +0200
+++ b/CMakeLists.txt	Fri Jun 24 12:01:33 2016 +0200
@@ -125,11 +125,11 @@
   )
 
 add_library(OrthancDicomWeb SHARED ${CORE_SOURCES}
+  ${CMAKE_SOURCE_DIR}/Plugin/DicomWebClient.cpp
   ${CMAKE_SOURCE_DIR}/Plugin/DicomWebServers.cpp
   ${CMAKE_SOURCE_DIR}/Plugin/Plugin.cpp
   ${CMAKE_SOURCE_DIR}/Plugin/QidoRs.cpp
   ${CMAKE_SOURCE_DIR}/Plugin/StowRs.cpp
-  ${CMAKE_SOURCE_DIR}/Plugin/StowRsClient.cpp
   ${CMAKE_SOURCE_DIR}/Plugin/WadoRs.cpp
   ${CMAKE_SOURCE_DIR}/Plugin/WadoRsRetrieveFrames.cpp
   ${CMAKE_SOURCE_DIR}/Plugin/WadoUri.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugin/DicomWebClient.cpp	Fri Jun 24 12:01:33 2016 +0200
@@ -0,0 +1,303 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "DicomWebClient.h"
+
+#include "Plugin.h"
+#include "DicomWebServers.h"
+
+#include <json/reader.h>
+#include <list>
+#include <boost/lexical_cast.hpp>
+
+#include "../Orthanc/Core/ChunkedBuffer.h"
+#include "../Orthanc/Core/Toolbox.h"
+
+
+static void AddInstance(std::list<std::string>& target,
+                        const Json::Value& instance)
+{
+  if (instance.type() != Json::objectValue ||
+      !instance.isMember("ID") ||
+      instance["ID"].type() != Json::stringValue)
+  {
+    throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+  }
+  else
+  {
+    target.push_back(instance["ID"].asString());
+  }
+}
+
+
+static bool GetSequenceSize(size_t& result,
+                            const Json::Value& answer,
+                            const std::string& tag,
+                            bool isMandatory,
+                            const std::string& server)
+{
+  const Json::Value* value = NULL;
+
+  std::string upper, lower;
+  Orthanc::Toolbox::ToUpperCase(upper, tag);
+  Orthanc::Toolbox::ToLowerCase(lower, tag);
+  
+  if (answer.isMember(upper))
+  {
+    value = &answer[upper];
+  }
+  else if (answer.isMember(lower))
+  {
+    value = &answer[lower];
+  }
+  else if (isMandatory)
+  {
+    OrthancPlugins::Configuration::LogError("The STOW-RS JSON response from DICOMweb server " + server + 
+                                            " does not contain the mandatory tag " + upper);
+    throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+  }
+  else
+  {
+    return false;
+  }
+
+  if (value->type() != Json::objectValue ||
+      !value->isMember("Value") ||
+      (*value) ["Value"].type() != Json::arrayValue)
+  {
+    OrthancPlugins::Configuration::LogError("Unable to parse STOW-RS JSON response from DICOMweb server " + server);
+    throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+  }
+
+  result = (*value) ["Value"].size();
+  return true;
+}
+
+
+
+static void ParseRestRequest(std::list<std::string>& instances /* out */,
+                             std::map<std::string, std::string>& httpHeaders /* out */,
+                             const OrthancPluginHttpRequest* request /* in */)
+{
+  static const char* RESOURCES = "Resources";
+  static const char* HTTP_HEADERS = "HttpHeaders";
+
+  Json::Value body;
+  Json::Reader reader;
+  if (!reader.parse(request->body, request->body + request->bodySize, body) ||
+      body.type() != Json::objectValue ||
+      !body.isMember(RESOURCES) ||
+      body[RESOURCES].type() != Json::arrayValue)
+  {
+    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);
+  }
+
+  OrthancPlugins::ParseAssociativeArray(httpHeaders, body, HTTP_HEADERS);
+
+  Json::Value& resources = body[RESOURCES];
+
+  // Extract information about all the child instances
+  for (Json::Value::ArrayIndex i = 0; i < resources.size(); i++)
+  {
+    if (resources[i].type() != Json::stringValue)
+    {
+      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
+    }
+
+    std::string resource = resources[i].asString();
+    if (resource.empty())
+    {
+      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_UnknownResource);
+    }
+
+    Json::Value tmp;
+    if (OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/instances/" + resource, false))
+    {
+      AddInstance(instances, tmp);
+    }
+    else if ((OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/series/" + resource, false) &&
+              OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/series/" + resource + "/instances", false)) ||
+             (OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/studies/" + resource, false) &&
+              OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/studies/" + resource + "/instances", false)) ||
+             (OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/patients/" + resource, false) &&
+              OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/patients/" + resource + "/instances", false)))
+    {
+      if (tmp.type() != Json::arrayValue)
+      {
+        throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+      }
+
+      for (Json::Value::ArrayIndex j = 0; j < tmp.size(); j++)
+      {
+        AddInstance(instances, tmp[j]);
+      }
+    }
+    else
+    {
+      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_UnknownResource);
+    }   
+  }
+}
+
+
+static void SendStowChunks(const Orthanc::WebServiceParameters& server,
+                           const std::map<std::string, std::string>& httpHeaders,
+                           const std::string& boundary,
+                           Orthanc::ChunkedBuffer& chunks,
+                           size_t& countInstances,
+                           bool force)
+{
+  if ((force && countInstances > 0) ||
+      countInstances > 10 /* TODO Parameter */ ||
+      chunks.GetNumBytes() > 10 * 1024 * 1024 /* TODO Parameter */)
+  {
+    chunks.AddChunk("\r\n--" + boundary + "--\r\n");
+
+    std::string body;
+    chunks.Flatten(body);
+
+    OrthancPlugins::MemoryBuffer answerBody(OrthancPlugins::Configuration::GetContext());
+    std::map<std::string, std::string> answerHeaders;
+    OrthancPlugins::CallServer(answerBody, answerHeaders, server, OrthancPluginHttpMethod_Post,
+                               httpHeaders, "studies", body);
+
+    Json::Value response;
+    Json::Reader reader;
+    bool success = reader.parse(reinterpret_cast<const char*>((*answerBody)->data),
+                                reinterpret_cast<const char*>((*answerBody)->data) + (*answerBody)->size, response);
+    answerBody.Clear();
+
+    if (!success ||
+        response.type() != Json::objectValue ||
+        !response.isMember("00081199"))
+    {
+      OrthancPlugins::Configuration::LogError("Unable to parse STOW-RS JSON response from DICOMweb server " + server.GetUrl());
+      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
+    }
+
+    size_t size;
+    if (!GetSequenceSize(size, response, "00081199", true, server.GetUrl()) ||
+        size != countInstances)
+    {
+      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);
+    }
+
+    if (GetSequenceSize(size, response, "00081198", false, server.GetUrl()) &&
+        size != 0)
+    {
+      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);    
+    }
+
+    if (GetSequenceSize(size, response, "0008119A", false, server.GetUrl()) &&
+        size != 0)
+    {
+      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);    
+    }
+
+    countInstances = 0;
+  }
+}
+
+
+void StowClient(OrthancPluginRestOutput* output,
+                const char* /*url*/,
+                const OrthancPluginHttpRequest* request)
+{
+  if (request->groupsCount != 1)
+  {
+    throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadRequest);
+  }
+
+  if (request->method != OrthancPluginHttpMethod_Post)
+  {
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "POST");
+    return;
+  }
+
+  Orthanc::WebServiceParameters server(OrthancPlugins::DicomWebServers::GetInstance().GetServer(request->groups[0]));
+
+  std::string boundary;
+
+  {
+    char* uuid = OrthancPluginGenerateUuid(OrthancPlugins::Configuration::GetContext());
+    try
+    {
+      boundary.assign(uuid);
+    }
+    catch (...)
+    {
+      OrthancPluginFreeString(OrthancPlugins::Configuration::GetContext(), uuid);
+      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NotEnoughMemory);
+    }
+
+    OrthancPluginFreeString(OrthancPlugins::Configuration::GetContext(), uuid);
+  }
+
+  std::string mime = "multipart/related; type=application/dicom; boundary=" + boundary;
+
+  std::map<std::string, std::string> httpHeaders;
+  httpHeaders["Accept"] = "application/json";
+  httpHeaders["Expect"] = "";
+  httpHeaders["Content-Type"] = mime;
+
+  std::list<std::string> instances;
+  ParseRestRequest(instances, httpHeaders, request);
+
+  {
+    OrthancPlugins::Configuration::LogInfo("Sending " + boost::lexical_cast<std::string>(instances.size()) + 
+                                           " instances using STOW-RS to DICOMweb server: " + server.GetUrl());
+  }
+
+  Orthanc::ChunkedBuffer chunks;
+  size_t countInstances = 0;
+
+  for (std::list<std::string>::const_iterator it = instances.begin(); it != instances.end(); it++)
+  {
+    std::string dicom;
+    if (OrthancPlugins::RestApiGetString(dicom, OrthancPlugins::Configuration::GetContext(), "/instances/" + *it + "/file"))
+    {
+      chunks.AddChunk("\r\n--" + boundary + "\r\n" +
+                      "Content-Type: application/dicom\r\n" +
+                      "Content-Length: " + boost::lexical_cast<std::string>(dicom.size()) +
+                      "\r\n\r\n");
+      chunks.AddChunk(dicom);
+      countInstances ++;
+
+      SendStowChunks(server, httpHeaders, boundary, chunks, countInstances, false);
+    }
+  }
+
+  SendStowChunks(server, httpHeaders, boundary, chunks, countInstances, true);
+
+  std::string answer = "{}\n";
+  OrthancPluginAnswerBuffer(OrthancPlugins::Configuration::GetContext(), output, answer.c_str(), answer.size(), "application/json");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugin/DicomWebClient.h	Fri Jun 24 12:01:33 2016 +0200
@@ -0,0 +1,28 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "Configuration.h"
+
+
+void StowClient(OrthancPluginRestOutput* output,
+                const char* url,
+                const OrthancPluginHttpRequest* request);
--- a/Plugin/Plugin.cpp	Fri Jun 24 11:56:33 2016 +0200
+++ b/Plugin/Plugin.cpp	Fri Jun 24 12:01:33 2016 +0200
@@ -22,7 +22,7 @@
 
 #include "QidoRs.h"
 #include "StowRs.h"
-#include "StowRsClient.h"
+#include "DicomWebClient.h"
 #include "WadoRs.h"
 #include "WadoUri.h"
 #include "Configuration.h"
--- a/Plugin/StowRsClient.cpp	Fri Jun 24 11:56:33 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,303 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "StowRsClient.h"
-
-#include "Plugin.h"
-#include "DicomWebServers.h"
-
-#include <json/reader.h>
-#include <list>
-#include <boost/lexical_cast.hpp>
-
-#include "../Orthanc/Core/ChunkedBuffer.h"
-#include "../Orthanc/Core/Toolbox.h"
-
-
-static void AddInstance(std::list<std::string>& target,
-                        const Json::Value& instance)
-{
-  if (instance.type() != Json::objectValue ||
-      !instance.isMember("ID") ||
-      instance["ID"].type() != Json::stringValue)
-  {
-    throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
-  }
-  else
-  {
-    target.push_back(instance["ID"].asString());
-  }
-}
-
-
-static bool GetSequenceSize(size_t& result,
-                            const Json::Value& answer,
-                            const std::string& tag,
-                            bool isMandatory,
-                            const std::string& server)
-{
-  const Json::Value* value = NULL;
-
-  std::string upper, lower;
-  Orthanc::Toolbox::ToUpperCase(upper, tag);
-  Orthanc::Toolbox::ToLowerCase(lower, tag);
-  
-  if (answer.isMember(upper))
-  {
-    value = &answer[upper];
-  }
-  else if (answer.isMember(lower))
-  {
-    value = &answer[lower];
-  }
-  else if (isMandatory)
-  {
-    OrthancPlugins::Configuration::LogError("The STOW-RS JSON response from DICOMweb server " + server + 
-                                            " does not contain the mandatory tag " + upper);
-    throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
-  }
-  else
-  {
-    return false;
-  }
-
-  if (value->type() != Json::objectValue ||
-      !value->isMember("Value") ||
-      (*value) ["Value"].type() != Json::arrayValue)
-  {
-    OrthancPlugins::Configuration::LogError("Unable to parse STOW-RS JSON response from DICOMweb server " + server);
-    throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
-  }
-
-  result = (*value) ["Value"].size();
-  return true;
-}
-
-
-
-static void ParseRestRequest(std::list<std::string>& instances /* out */,
-                             std::map<std::string, std::string>& httpHeaders /* out */,
-                             const OrthancPluginHttpRequest* request /* in */)
-{
-  static const char* RESOURCES = "Resources";
-  static const char* HTTP_HEADERS = "HttpHeaders";
-
-  Json::Value body;
-  Json::Reader reader;
-  if (!reader.parse(request->body, request->body + request->bodySize, body) ||
-      body.type() != Json::objectValue ||
-      !body.isMember(RESOURCES) ||
-      body[RESOURCES].type() != Json::arrayValue)
-  {
-    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);
-  }
-
-  OrthancPlugins::ParseAssociativeArray(httpHeaders, body, HTTP_HEADERS);
-
-  Json::Value& resources = body[RESOURCES];
-
-  // Extract information about all the child instances
-  for (Json::Value::ArrayIndex i = 0; i < resources.size(); i++)
-  {
-    if (resources[i].type() != Json::stringValue)
-    {
-      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadFileFormat);
-    }
-
-    std::string resource = resources[i].asString();
-    if (resource.empty())
-    {
-      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_UnknownResource);
-    }
-
-    Json::Value tmp;
-    if (OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/instances/" + resource, false))
-    {
-      AddInstance(instances, tmp);
-    }
-    else if ((OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/series/" + resource, false) &&
-              OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/series/" + resource + "/instances", false)) ||
-             (OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/studies/" + resource, false) &&
-              OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/studies/" + resource + "/instances", false)) ||
-             (OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/patients/" + resource, false) &&
-              OrthancPlugins::RestApiGetJson(tmp, OrthancPlugins::Configuration::GetContext(), "/patients/" + resource + "/instances", false)))
-    {
-      if (tmp.type() != Json::arrayValue)
-      {
-        throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
-      }
-
-      for (Json::Value::ArrayIndex j = 0; j < tmp.size(); j++)
-      {
-        AddInstance(instances, tmp[j]);
-      }
-    }
-    else
-    {
-      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_UnknownResource);
-    }   
-  }
-}
-
-
-static void SendStowChunks(const Orthanc::WebServiceParameters& server,
-                           const std::map<std::string, std::string>& httpHeaders,
-                           const std::string& boundary,
-                           Orthanc::ChunkedBuffer& chunks,
-                           size_t& countInstances,
-                           bool force)
-{
-  if ((force && countInstances > 0) ||
-      countInstances > 10 /* TODO Parameter */ ||
-      chunks.GetNumBytes() > 10 * 1024 * 1024 /* TODO Parameter */)
-  {
-    chunks.AddChunk("\r\n--" + boundary + "--\r\n");
-
-    std::string body;
-    chunks.Flatten(body);
-
-    OrthancPlugins::MemoryBuffer answerBody(OrthancPlugins::Configuration::GetContext());
-    std::map<std::string, std::string> answerHeaders;
-    OrthancPlugins::CallServer(answerBody, answerHeaders, server, OrthancPluginHttpMethod_Post,
-                               httpHeaders, "studies", body);
-
-    Json::Value response;
-    Json::Reader reader;
-    bool success = reader.parse(reinterpret_cast<const char*>((*answerBody)->data),
-                                reinterpret_cast<const char*>((*answerBody)->data) + (*answerBody)->size, response);
-    answerBody.Clear();
-
-    if (!success ||
-        response.type() != Json::objectValue ||
-        !response.isMember("00081199"))
-    {
-      OrthancPlugins::Configuration::LogError("Unable to parse STOW-RS JSON response from DICOMweb server " + server.GetUrl());
-      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NetworkProtocol);
-    }
-
-    size_t size;
-    if (!GetSequenceSize(size, response, "00081199", true, server.GetUrl()) ||
-        size != countInstances)
-    {
-      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);
-    }
-
-    if (GetSequenceSize(size, response, "00081198", false, server.GetUrl()) &&
-        size != 0)
-    {
-      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);    
-    }
-
-    if (GetSequenceSize(size, response, "0008119A", false, server.GetUrl()) &&
-        size != 0)
-    {
-      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);    
-    }
-
-    countInstances = 0;
-  }
-}
-
-
-void StowClient(OrthancPluginRestOutput* output,
-                const char* /*url*/,
-                const OrthancPluginHttpRequest* request)
-{
-  if (request->groupsCount != 1)
-  {
-    throw OrthancPlugins::PluginException(OrthancPluginErrorCode_BadRequest);
-  }
-
-  if (request->method != OrthancPluginHttpMethod_Post)
-  {
-    OrthancPluginSendMethodNotAllowed(OrthancPlugins::Configuration::GetContext(), output, "POST");
-    return;
-  }
-
-  Orthanc::WebServiceParameters server(OrthancPlugins::DicomWebServers::GetInstance().GetServer(request->groups[0]));
-
-  std::string boundary;
-
-  {
-    char* uuid = OrthancPluginGenerateUuid(OrthancPlugins::Configuration::GetContext());
-    try
-    {
-      boundary.assign(uuid);
-    }
-    catch (...)
-    {
-      OrthancPluginFreeString(OrthancPlugins::Configuration::GetContext(), uuid);
-      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_NotEnoughMemory);
-    }
-
-    OrthancPluginFreeString(OrthancPlugins::Configuration::GetContext(), uuid);
-  }
-
-  std::string mime = "multipart/related; type=application/dicom; boundary=" + boundary;
-
-  std::map<std::string, std::string> httpHeaders;
-  httpHeaders["Accept"] = "application/json";
-  httpHeaders["Expect"] = "";
-  httpHeaders["Content-Type"] = mime;
-
-  std::list<std::string> instances;
-  ParseRestRequest(instances, httpHeaders, request);
-
-  {
-    OrthancPlugins::Configuration::LogInfo("Sending " + boost::lexical_cast<std::string>(instances.size()) + 
-                                           " instances using STOW-RS to DICOMweb server: " + server.GetUrl());
-  }
-
-  Orthanc::ChunkedBuffer chunks;
-  size_t countInstances = 0;
-
-  for (std::list<std::string>::const_iterator it = instances.begin(); it != instances.end(); it++)
-  {
-    std::string dicom;
-    if (OrthancPlugins::RestApiGetString(dicom, OrthancPlugins::Configuration::GetContext(), "/instances/" + *it + "/file"))
-    {
-      chunks.AddChunk("\r\n--" + boundary + "\r\n" +
-                      "Content-Type: application/dicom\r\n" +
-                      "Content-Length: " + boost::lexical_cast<std::string>(dicom.size()) +
-                      "\r\n\r\n");
-      chunks.AddChunk(dicom);
-      countInstances ++;
-
-      SendStowChunks(server, httpHeaders, boundary, chunks, countInstances, false);
-    }
-  }
-
-  SendStowChunks(server, httpHeaders, boundary, chunks, countInstances, true);
-
-  std::string answer = "{}\n";
-  OrthancPluginAnswerBuffer(OrthancPlugins::Configuration::GetContext(), output, answer.c_str(), answer.size(), "application/json");
-}
--- a/Plugin/StowRsClient.h	Fri Jun 24 11:56:33 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "Configuration.h"
-
-
-void StowClient(OrthancPluginRestOutput* output,
-                const char* url,
-                const OrthancPluginHttpRequest* request);