Orthanc/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
2025-06-23 19:07:37 +05:30

4569 lines
115 KiB
C++

/**
* Orthanc - A Lightweight, RESTful DICOM Store
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
* Department, University Hospital of Liege, Belgium
* Copyright (C) 2017-2023 Osimis S.A., Belgium
* Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
* Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, 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.
*
* 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 "OrthancPluginCppWrapper.h"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/move/unique_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/algorithm/string/join.hpp>
#include <json/reader.h>
#include <json/version.h>
#include <json/writer.h>
#if !defined(JSONCPP_VERSION_MAJOR) || !defined(JSONCPP_VERSION_MINOR)
# error Cannot access the version of JsonCpp
#endif
/**
* We use deprecated "Json::Reader", "Json::StyledWriter" and
* "Json::FastWriter" if JsonCpp < 1.7.0. This choice is rather
* arbitrary, but if Json >= 1.9.0, gcc generates explicit deprecation
* warnings (clang was warning in earlier versions). For reference,
* these classes seem to have been deprecated since JsonCpp 1.4.0 (on
* February 2015) by the following changeset:
* https://github.com/open-source-parsers/jsoncpp/commit/8df98f6112890d6272734975dd6d70cf8999bb22
**/
#if (JSONCPP_VERSION_MAJOR >= 2 || \
(JSONCPP_VERSION_MAJOR == 1 && JSONCPP_VERSION_MINOR >= 8))
# define JSONCPP_USE_DEPRECATED 0
#else
# define JSONCPP_USE_DEPRECATED 1
#endif
#if !ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 0)
static const OrthancPluginErrorCode OrthancPluginErrorCode_NullPointer = OrthancPluginErrorCode_Plugin;
#endif
namespace OrthancPlugins
{
static OrthancPluginContext* globalContext_ = NULL;
static std::string pluginName_;
void SetGlobalContext(OrthancPluginContext* context)
{
if (context == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(NullPointer);
}
else if (globalContext_ == NULL)
{
globalContext_ = context;
}
else
{
ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
}
}
void SetGlobalContext(OrthancPluginContext* context,
const char* pluginName)
{
SetGlobalContext(context);
pluginName_ = pluginName;
}
void ResetGlobalContext()
{
globalContext_ = NULL;
pluginName_.clear();
}
bool HasGlobalContext()
{
return globalContext_ != NULL;
}
OrthancPluginContext* GetGlobalContext()
{
if (globalContext_ == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
}
else
{
return globalContext_;
}
}
#if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1
void LogMessage(OrthancPluginLogLevel level,
const char* file,
uint32_t line,
const std::string& message)
{
if (HasGlobalContext())
{
#if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1
const char* pluginName = (pluginName_.empty() ? NULL : pluginName_.c_str());
OrthancPluginLogMessage(GetGlobalContext(), message.c_str(), pluginName, file, line, OrthancPluginLogCategory_Generic, level);
#else
switch (level)
{
case OrthancPluginLogLevel_Error:
OrthancPluginLogError(GetGlobalContext(), message.c_str());
break;
case OrthancPluginLogLevel_Warning:
OrthancPluginLogWarning(GetGlobalContext(), message.c_str());
break;
case OrthancPluginLogLevel_Info:
OrthancPluginLogInfo(GetGlobalContext(), message.c_str());
break;
default:
ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
#endif
}
}
#endif
void LogError(const std::string& message)
{
if (HasGlobalContext())
{
OrthancPluginLogError(GetGlobalContext(), message.c_str());
}
}
void LogWarning(const std::string& message)
{
if (HasGlobalContext())
{
OrthancPluginLogWarning(GetGlobalContext(), message.c_str());
}
}
void LogInfo(const std::string& message)
{
if (HasGlobalContext())
{
OrthancPluginLogInfo(GetGlobalContext(), message.c_str());
}
}
void MemoryBuffer::Check(OrthancPluginErrorCode code)
{
if (code != OrthancPluginErrorCode_Success)
{
// 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
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
}
MemoryBuffer::MemoryBuffer()
{
buffer_.data = NULL;
buffer_.size = 0;
}
void MemoryBuffer::Clear()
{
if (buffer_.data != NULL)
{
OrthancPluginFreeMemoryBuffer(GetGlobalContext(), &buffer_);
buffer_.data = NULL;
buffer_.size = 0;
}
}
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
void MemoryBuffer::Assign(const void* buffer,
size_t size)
{
uint32_t s = static_cast<uint32_t>(size);
if (static_cast<size_t>(s) != size)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory);
}
Clear();
if (OrthancPluginCreateMemoryBuffer(GetGlobalContext(), &buffer_, s) !=
OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory);
}
else
{
if (size > 0)
{
memcpy(buffer_.data, buffer, size);
}
}
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
void MemoryBuffer::Assign(const std::string& s)
{
Assign(s.empty() ? NULL : s.c_str(), s.size());
}
#endif
void MemoryBuffer::Assign(OrthancPluginMemoryBuffer& other)
{
Clear();
buffer_.data = other.data;
buffer_.size = other.size;
other.data = NULL;
other.size = 0;
}
void MemoryBuffer::Swap(MemoryBuffer& other)
{
std::swap(buffer_.data, other.buffer_.data);
std::swap(buffer_.size, other.buffer_.size);
}
OrthancPluginMemoryBuffer MemoryBuffer::Release()
{
OrthancPluginMemoryBuffer result = buffer_;
buffer_.data = NULL;
buffer_.size = 0;
return result;
}
void MemoryBuffer::ToString(std::string& target) const
{
if (buffer_.size == 0)
{
target.clear();
}
else
{
target.assign(reinterpret_cast<const char*>(buffer_.data), buffer_.size);
}
}
void MemoryBuffer::ToJson(Json::Value& target) const
{
if (buffer_.data == NULL ||
buffer_.size == 0)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
if (!ReadJson(target, buffer_.data, buffer_.size))
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot convert some memory buffer to JSON");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
}
bool MemoryBuffer::RestApiGet(const std::string& uri,
bool applyPlugins)
{
Clear();
if (applyPlugins)
{
return CheckHttp(OrthancPluginRestApiGetAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str()));
}
else
{
return CheckHttp(OrthancPluginRestApiGet(GetGlobalContext(), &buffer_, uri.c_str()));
}
}
// helper class to convert std::map of headers to the plugin SDK C structure
class PluginHttpHeaders
{
private:
std::vector<const char*> headersKeys_;
std::vector<const char*> headersValues_;
public:
explicit PluginHttpHeaders(const HttpHeaders& httpHeaders)
{
for (HttpHeaders::const_iterator
it = httpHeaders.begin(); it != httpHeaders.end(); ++it)
{
headersKeys_.push_back(it->first.c_str());
headersValues_.push_back(it->second.c_str());
}
}
const char* const* GetKeys()
{
return (headersKeys_.empty() ? NULL : &headersKeys_[0]);
}
const char* const* GetValues()
{
return (headersValues_.empty() ? NULL : &headersValues_[0]);
}
uint32_t GetSize()
{
return static_cast<uint32_t>(headersKeys_.size());
}
};
bool MemoryBuffer::RestApiGet(const std::string& uri,
const HttpHeaders& httpHeaders,
bool applyPlugins)
{
Clear();
PluginHttpHeaders headers(httpHeaders);
return CheckHttp(OrthancPluginRestApiGet2(
GetGlobalContext(), &buffer_, uri.c_str(),
headers.GetSize(),
headers.GetKeys(),
headers.GetValues(), applyPlugins));
}
bool MemoryBuffer::RestApiPost(const std::string& uri,
const void* body,
size_t bodySize,
bool applyPlugins)
{
Clear();
// Cast for compatibility with Orthanc SDK <= 1.5.6
const char* b = reinterpret_cast<const char*>(body);
if (applyPlugins)
{
return CheckHttp(OrthancPluginRestApiPostAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
}
else
{
return CheckHttp(OrthancPluginRestApiPost(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
}
}
#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
bool MemoryBuffer::RestApiPost(const std::string& uri,
const void* body,
size_t bodySize,
const HttpHeaders& httpHeaders,
bool applyPlugins)
{
MemoryBuffer answerHeaders;
uint16_t httpStatus;
PluginHttpHeaders headers(httpHeaders);
return CheckHttp(OrthancPluginCallRestApi(GetGlobalContext(),
&buffer_,
*answerHeaders,
&httpStatus,
OrthancPluginHttpMethod_Post,
uri.c_str(),
headers.GetSize(), headers.GetKeys(), headers.GetValues(),
body, bodySize,
applyPlugins));
}
bool MemoryBuffer::RestApiPost(const std::string& uri,
const Json::Value& body,
const HttpHeaders& httpHeaders,
bool applyPlugins)
{
std::string s;
WriteFastJson(s, body);
return RestApiPost(uri, s.c_str(), s.size(), httpHeaders, applyPlugins);
}
#endif
bool MemoryBuffer::RestApiPut(const std::string& uri,
const void* body,
size_t bodySize,
bool applyPlugins)
{
Clear();
// Cast for compatibility with Orthanc SDK <= 1.5.6
const char* b = reinterpret_cast<const char*>(body);
if (applyPlugins)
{
return CheckHttp(OrthancPluginRestApiPutAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
}
else
{
return CheckHttp(OrthancPluginRestApiPut(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
}
}
static bool ReadJsonInternal(Json::Value& target,
const void* buffer,
size_t size,
bool collectComments)
{
#if JSONCPP_USE_DEPRECATED == 1
Json::Reader reader;
return reader.parse(reinterpret_cast<const char*>(buffer),
reinterpret_cast<const char*>(buffer) + size, target, collectComments);
#else
Json::CharReaderBuilder builder;
builder.settings_["collectComments"] = collectComments;
const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
assert(reader.get() != NULL);
JSONCPP_STRING err;
if (reader->parse(reinterpret_cast<const char*>(buffer),
reinterpret_cast<const char*>(buffer) + size, &target, &err))
{
return true;
}
else
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot parse JSON: " + std::string(err));
return false;
}
#endif
}
bool ReadJson(Json::Value& target,
const std::string& source)
{
return ReadJson(target, source.empty() ? NULL : source.c_str(), source.size());
}
bool ReadJson(Json::Value& target,
const void* buffer,
size_t size)
{
return ReadJsonInternal(target, buffer, size, true);
}
bool ReadJsonWithoutComments(Json::Value& target,
const std::string& source)
{
return ReadJsonWithoutComments(target, source.empty() ? NULL : source.c_str(), source.size());
}
bool ReadJsonWithoutComments(Json::Value& target,
const void* buffer,
size_t size)
{
return ReadJsonInternal(target, buffer, size, false);
}
void WriteFastJson(std::string& target,
const Json::Value& source)
{
#if JSONCPP_USE_DEPRECATED == 1
Json::FastWriter writer;
target = writer.write(source);
#else
Json::StreamWriterBuilder builder;
builder.settings_["indentation"] = "";
target = Json::writeString(builder, source);
#endif
}
void WriteStyledJson(std::string& target,
const Json::Value& source)
{
#if JSONCPP_USE_DEPRECATED == 1
Json::StyledWriter writer;
target = writer.write(source);
#else
Json::StreamWriterBuilder builder;
builder.settings_["indentation"] = " ";
target = Json::writeString(builder, source);
#endif
}
bool MemoryBuffer::RestApiPost(const std::string& uri,
const Json::Value& body,
bool applyPlugins)
{
std::string s;
WriteFastJson(s, body);
return RestApiPost(uri, s, applyPlugins);
}
bool MemoryBuffer::RestApiPut(const std::string& uri,
const Json::Value& body,
bool applyPlugins)
{
std::string s;
WriteFastJson(s, body);
return RestApiPut(uri, s, applyPlugins);
}
void MemoryBuffer::CreateDicom(const Json::Value& tags,
OrthancPluginCreateDicomFlags flags)
{
Clear();
std::string s;
WriteFastJson(s, tags);
Check(OrthancPluginCreateDicom(GetGlobalContext(), &buffer_, s.c_str(), NULL, flags));
}
void MemoryBuffer::CreateDicom(const Json::Value& tags,
const OrthancImage& pixelData,
OrthancPluginCreateDicomFlags flags)
{
Clear();
std::string s;
WriteFastJson(s, tags);
Check(OrthancPluginCreateDicom(GetGlobalContext(), &buffer_, s.c_str(), pixelData.GetObject(), flags));
}
void MemoryBuffer::ReadFile(const std::string& path)
{
Clear();
Check(OrthancPluginReadFile(GetGlobalContext(), &buffer_, path.c_str()));
}
void MemoryBuffer::GetDicomQuery(const OrthancPluginWorklistQuery* query)
{
Clear();
Check(OrthancPluginWorklistGetDicomQuery(GetGlobalContext(), &buffer_, query));
}
void OrthancString::Assign(char* str)
{
Clear();
if (str != NULL)
{
str_ = str;
}
}
void OrthancString::Clear()
{
if (str_ != NULL)
{
OrthancPluginFreeString(GetGlobalContext(), str_);
str_ = NULL;
}
}
void OrthancString::ToString(std::string& target) const
{
if (str_ == NULL)
{
target.clear();
}
else
{
target.assign(str_);
}
}
void OrthancString::ToJson(Json::Value& target) const
{
if (str_ == NULL)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot convert an empty memory buffer to JSON");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
if (!ReadJson(target, str_))
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot convert some memory buffer to JSON");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
}
void OrthancString::ToJsonWithoutComments(Json::Value& target) const
{
if (str_ == NULL)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot convert an empty memory buffer to JSON");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
if (!ReadJsonWithoutComments(target, str_))
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot convert some memory buffer to JSON");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
}
void MemoryBuffer::DicomToJson(Json::Value& target,
OrthancPluginDicomToJsonFormat format,
OrthancPluginDicomToJsonFlags flags,
uint32_t maxStringLength)
{
OrthancString str;
str.Assign(OrthancPluginDicomBufferToJson
(GetGlobalContext(), reinterpret_cast<const char*>(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(GetGlobalContext(), &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();
if (body.size() > 0xffffffffu)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot handle body size > 4GB");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
return CheckHttp(OrthancPluginHttpPost(GetGlobalContext(), &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();
if (body.size() > 0xffffffffu)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot handle body size > 4GB");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
return CheckHttp(OrthancPluginHttpPut(GetGlobalContext(), &buffer_, url.c_str(),
body.empty() ? NULL : body.c_str(),
body.size(),
username.empty() ? NULL : username.c_str(),
password.empty() ? NULL : password.c_str()));
}
void MemoryBuffer::GetDicomInstance(const std::string& instanceId)
{
Clear();
Check(OrthancPluginGetDicomForInstance(GetGlobalContext(), &buffer_, instanceId.c_str()));
}
bool HttpDelete(const std::string& url,
const std::string& username,
const std::string& password)
{
OrthancPluginErrorCode error = OrthancPluginHttpDelete
(GetGlobalContext(), 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);
}
}
void OrthancConfiguration::LoadConfiguration()
{
OrthancString str;
str.Assign(OrthancPluginGetConfiguration(GetGlobalContext()));
if (str.GetContent() == NULL)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot access the Orthanc configuration");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
str.ToJsonWithoutComments(configuration_);
if (configuration_.type() != Json::objectValue)
{
ORTHANC_PLUGINS_LOG_ERROR("Unable to read the Orthanc configuration");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
}
OrthancConfiguration::OrthancConfiguration()
{
LoadConfiguration();
}
OrthancConfiguration::OrthancConfiguration(bool loadConfiguration)
{
if (loadConfiguration)
{
LoadConfiguration();
}
else
{
configuration_ = Json::objectValue;
}
}
OrthancConfiguration::OrthancConfiguration(const Json::Value& configuration, const std::string& path) :
configuration_(configuration),
path_(path)
{
}
std::string OrthancConfiguration::GetPath(const std::string& key) const
{
if (path_.empty())
{
return key;
}
else
{
return path_ + "." + key;
}
}
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
{
assert(configuration_.type() == Json::objectValue);
target.path_ = GetPath(key);
if (!configuration_.isMember(key))
{
target.configuration_ = Json::objectValue;
}
else
{
if (configuration_[key].type() != Json::objectValue)
{
ORTHANC_PLUGINS_LOG_ERROR("The configuration section \"" + target.path_ +
"\" is not an associative array as expected");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
target.configuration_ = configuration_[key];
}
}
bool OrthancConfiguration::LookupStringValue(std::string& target,
const std::string& key) const
{
assert(configuration_.type() == Json::objectValue);
if (!configuration_.isMember(key))
{
return false;
}
if (configuration_[key].type() != Json::stringValue)
{
ORTHANC_PLUGINS_LOG_ERROR("The configuration option \"" + GetPath(key) +
"\" is not a string as expected");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
target = configuration_[key].asString();
return true;
}
bool OrthancConfiguration::LookupIntegerValue(int& target,
const std::string& key) const
{
assert(configuration_.type() == Json::objectValue);
if (!configuration_.isMember(key))
{
return false;
}
switch (configuration_[key].type())
{
case Json::intValue:
target = configuration_[key].asInt();
return true;
case Json::uintValue:
target = configuration_[key].asUInt();
return true;
default:
ORTHANC_PLUGINS_LOG_ERROR("The configuration option \"" + GetPath(key) +
"\" is not an integer as expected");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
}
bool OrthancConfiguration::LookupUnsignedIntegerValue(unsigned int& target,
const std::string& key) const
{
int tmp;
if (!LookupIntegerValue(tmp, key))
{
return false;
}
if (tmp < 0)
{
ORTHANC_PLUGINS_LOG_ERROR("The configuration option \"" + GetPath(key) +
"\" is not a positive integer as expected");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
else
{
target = static_cast<unsigned int>(tmp);
return true;
}
}
bool OrthancConfiguration::LookupBooleanValue(bool& target,
const std::string& key) const
{
assert(configuration_.type() == Json::objectValue);
if (!configuration_.isMember(key))
{
return false;
}
if (configuration_[key].type() != Json::booleanValue)
{
ORTHANC_PLUGINS_LOG_ERROR("The configuration option \"" + GetPath(key) +
"\" is not a Boolean as expected");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
target = configuration_[key].asBool();
return true;
}
bool OrthancConfiguration::LookupFloatValue(float& target,
const std::string& key) const
{
assert(configuration_.type() == Json::objectValue);
if (!configuration_.isMember(key))
{
return false;
}
switch (configuration_[key].type())
{
case Json::realValue:
target = configuration_[key].asFloat();
return true;
case Json::intValue:
target = static_cast<float>(configuration_[key].asInt());
return true;
case Json::uintValue:
target = static_cast<float>(configuration_[key].asUInt());
return true;
default:
ORTHANC_PLUGINS_LOG_ERROR("The configuration option \"" + GetPath(key) +
"\" is not an integer as expected");
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;
}
ORTHANC_PLUGINS_LOG_ERROR("The configuration option \"" + GetPath(key) +
"\" is not a list of strings as expected");
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;
}
}
std::string OrthancConfiguration::GetStringValue(const std::string& key,
const std::string& defaultValue) const
{
std::string tmp;
if (LookupStringValue(tmp, key))
{
return tmp;
}
else
{
return defaultValue;
}
}
int OrthancConfiguration::GetIntegerValue(const std::string& key,
int defaultValue) const
{
int tmp;
if (LookupIntegerValue(tmp, key))
{
return tmp;
}
else
{
return defaultValue;
}
}
unsigned int OrthancConfiguration::GetUnsignedIntegerValue(const std::string& key,
unsigned int defaultValue) const
{
unsigned int tmp;
if (LookupUnsignedIntegerValue(tmp, key))
{
return tmp;
}
else
{
return defaultValue;
}
}
bool OrthancConfiguration::GetBooleanValue(const std::string& key,
bool defaultValue) const
{
bool tmp;
if (LookupBooleanValue(tmp, key))
{
return tmp;
}
else
{
return defaultValue;
}
}
float OrthancConfiguration::GetFloatValue(const std::string& key,
float defaultValue) const
{
float tmp;
if (LookupFloatValue(tmp, key))
{
return tmp;
}
else
{
return defaultValue;
}
}
void OrthancConfiguration::GetDictionary(std::map<std::string, std::string>& target,
const std::string& key) const
{
assert(configuration_.type() == Json::objectValue);
target.clear();
if (!configuration_.isMember(key))
{
return;
}
if (configuration_[key].type() != Json::objectValue)
{
ORTHANC_PLUGINS_LOG_ERROR("The configuration option \"" + GetPath(key) +
"\" is not an object as expected");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
Json::Value::Members members = configuration_[key].getMemberNames();
for (size_t i = 0; i < members.size(); i++)
{
const Json::Value& value = configuration_[key][members[i]];
if (value.type() == Json::stringValue)
{
target[members[i]] = value.asString();
}
else
{
ORTHANC_PLUGINS_LOG_ERROR("The configuration option \"" + GetPath(key) +
"\" is not a dictionary mapping strings to strings");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
}
}
void OrthancImage::Clear()
{
if (image_ != NULL)
{
OrthancPluginFreeImage(GetGlobalContext(), image_);
image_ = NULL;
}
}
void OrthancImage::CheckImageAvailable() const
{
if (image_ == NULL)
{
ORTHANC_PLUGINS_LOG_ERROR("Trying to access a NULL image");
ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
OrthancImage::OrthancImage() :
image_(NULL)
{
}
OrthancImage::OrthancImage(OrthancPluginImage* image) :
image_(image)
{
}
OrthancImage::OrthancImage(OrthancPluginPixelFormat format,
uint32_t width,
uint32_t height)
{
image_ = OrthancPluginCreateImage(GetGlobalContext(), format, width, height);
if (image_ == NULL)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot create an image");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
}
OrthancImage::OrthancImage(OrthancPluginPixelFormat format,
uint32_t width,
uint32_t height,
uint32_t pitch,
void* buffer)
{
image_ = OrthancPluginCreateImageAccessor
(GetGlobalContext(), format, width, height, pitch, buffer);
if (image_ == NULL)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot create an image accessor");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
}
void OrthancImage::UncompressPngImage(const void* data,
size_t size)
{
Clear();
image_ = OrthancPluginUncompressImage(GetGlobalContext(), data, size, OrthancPluginImageFormat_Png);
if (image_ == NULL)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot uncompress a PNG image");
ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
void OrthancImage::UncompressJpegImage(const void* data,
size_t size)
{
Clear();
image_ = OrthancPluginUncompressImage(GetGlobalContext(), data, size, OrthancPluginImageFormat_Jpeg);
if (image_ == NULL)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot uncompress a JPEG image");
ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
void OrthancImage::DecodeDicomImage(const void* data,
size_t size,
unsigned int frame)
{
Clear();
image_ = OrthancPluginDecodeDicomImage(GetGlobalContext(), data, size, frame);
if (image_ == NULL)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot uncompress a DICOM image");
ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
OrthancPluginPixelFormat OrthancImage::GetPixelFormat() const
{
CheckImageAvailable();
return OrthancPluginGetImagePixelFormat(GetGlobalContext(), image_);
}
unsigned int OrthancImage::GetWidth() const
{
CheckImageAvailable();
return OrthancPluginGetImageWidth(GetGlobalContext(), image_);
}
unsigned int OrthancImage::GetHeight() const
{
CheckImageAvailable();
return OrthancPluginGetImageHeight(GetGlobalContext(), image_);
}
unsigned int OrthancImage::GetPitch() const
{
CheckImageAvailable();
return OrthancPluginGetImagePitch(GetGlobalContext(), image_);
}
void* OrthancImage::GetBuffer() const
{
CheckImageAvailable();
return OrthancPluginGetImageBuffer(GetGlobalContext(), image_);
}
void OrthancImage::CompressPngImage(MemoryBuffer& target) const
{
CheckImageAvailable();
OrthancPlugins::MemoryBuffer answer;
OrthancPluginCompressPngImage(GetGlobalContext(), *answer, GetPixelFormat(),
GetWidth(), GetHeight(), GetPitch(), GetBuffer());
target.Swap(answer);
}
void OrthancImage::CompressJpegImage(MemoryBuffer& target,
uint8_t quality) const
{
CheckImageAvailable();
OrthancPlugins::MemoryBuffer answer;
OrthancPluginCompressJpegImage(GetGlobalContext(), *answer, GetPixelFormat(),
GetWidth(), GetHeight(), GetPitch(), GetBuffer(), quality);
target.Swap(answer);
}
void OrthancImage::AnswerPngImage(OrthancPluginRestOutput* output) const
{
CheckImageAvailable();
OrthancPluginCompressAndAnswerPngImage(GetGlobalContext(), output, GetPixelFormat(),
GetWidth(), GetHeight(), GetPitch(), GetBuffer());
}
void OrthancImage::AnswerJpegImage(OrthancPluginRestOutput* output,
uint8_t quality) const
{
CheckImageAvailable();
OrthancPluginCompressAndAnswerJpegImage(GetGlobalContext(), output, GetPixelFormat(),
GetWidth(), GetHeight(), GetPitch(), GetBuffer(), quality);
}
OrthancPluginImage* OrthancImage::Release()
{
CheckImageAvailable();
OrthancPluginImage* tmp = image_;
image_ = NULL;
return tmp;
}
#if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
FindMatcher::FindMatcher(const OrthancPluginWorklistQuery* worklist) :
matcher_(NULL),
worklist_(worklist)
{
if (worklist_ == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
}
}
void FindMatcher::SetupDicom(const void* query,
uint32_t size)
{
worklist_ = NULL;
matcher_ = OrthancPluginCreateFindMatcher(GetGlobalContext(), query, size);
if (matcher_ == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
}
FindMatcher::~FindMatcher()
{
// The "worklist_" field
if (matcher_ != NULL)
{
OrthancPluginFreeFindMatcher(GetGlobalContext(), matcher_);
}
}
bool FindMatcher::IsMatch(const void* dicom,
uint32_t size) const
{
int32_t result;
if (matcher_ != NULL)
{
result = OrthancPluginFindMatcherIsMatch(GetGlobalContext(), matcher_, dicom, size);
}
else if (worklist_ != NULL)
{
result = OrthancPluginWorklistIsMatch(GetGlobalContext(), 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 */
void AnswerJson(const Json::Value& value,
OrthancPluginRestOutput* output)
{
std::string bodyString;
WriteStyledJson(bodyString, value);
OrthancPluginAnswerBuffer(GetGlobalContext(), output, bodyString.c_str(), bodyString.size(), "application/json");
}
void AnswerString(const std::string& answer,
const char* mimeType,
OrthancPluginRestOutput* output)
{
OrthancPluginAnswerBuffer(GetGlobalContext(), output, answer.c_str(), answer.size(), mimeType);
}
void AnswerHttpError(uint16_t httpError, OrthancPluginRestOutput *output)
{
OrthancPluginSendHttpStatusCode(GetGlobalContext(), output, httpError);
}
void AnswerMethodNotAllowed(OrthancPluginRestOutput *output, const char* allowedMethods)
{
OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowedMethods);
}
bool RestApiGetString(std::string& result,
const std::string& uri,
bool applyPlugins)
{
MemoryBuffer answer;
if (!answer.RestApiGet(uri, applyPlugins))
{
return false;
}
else
{
answer.ToString(result);
return true;
}
}
bool RestApiGetString(std::string& result,
const std::string& uri,
const HttpHeaders& httpHeaders,
bool applyPlugins)
{
MemoryBuffer answer;
if (!answer.RestApiGet(uri, httpHeaders, applyPlugins))
{
return false;
}
else
{
answer.ToString(result);
return true;
}
}
bool RestApiGet(Json::Value& result,
const std::string& uri,
const HttpHeaders& httpHeaders,
bool applyPlugins)
{
MemoryBuffer answer;
if (!answer.RestApiGet(uri, httpHeaders, applyPlugins))
{
return false;
}
else
{
if (!answer.IsEmpty())
{
answer.ToJson(result);
}
return true;
}
}
bool RestApiGet(Json::Value& result,
const std::string& uri,
bool applyPlugins)
{
MemoryBuffer answer;
if (!answer.RestApiGet(uri, applyPlugins))
{
return false;
}
else
{
if (!answer.IsEmpty())
{
answer.ToJson(result);
}
return true;
}
}
bool RestApiPost(std::string& result,
const std::string& uri,
const void* body,
size_t bodySize,
bool applyPlugins)
{
MemoryBuffer answer;
if (!answer.RestApiPost(uri, body, bodySize, applyPlugins))
{
return false;
}
else
{
if (!answer.IsEmpty())
{
result.assign(reinterpret_cast<const char*>(answer.GetData()), answer.GetSize());
}
return true;
}
}
bool RestApiPost(Json::Value& result,
const std::string& uri,
const void* body,
size_t bodySize,
bool applyPlugins)
{
MemoryBuffer answer;
if (!answer.RestApiPost(uri, body, bodySize, applyPlugins))
{
return false;
}
else
{
if (!answer.IsEmpty())
{
answer.ToJson(result);
}
return true;
}
}
#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
bool RestApiPost(Json::Value& result,
const std::string& uri,
const Json::Value& body,
const HttpHeaders& httpHeaders,
bool applyPlugins)
{
MemoryBuffer answer;
if (!answer.RestApiPost(uri, body, httpHeaders, applyPlugins))
{
return false;
}
else
{
if (!answer.IsEmpty())
{
answer.ToJson(result);
}
return true;
}
}
#endif
bool RestApiPost(Json::Value& result,
const std::string& uri,
const Json::Value& body,
bool applyPlugins)
{
std::string s;
WriteFastJson(s, body);
return RestApiPost(result, uri, s, applyPlugins);
}
bool RestApiPut(Json::Value& result,
const std::string& uri,
const void* body,
size_t bodySize,
bool applyPlugins)
{
MemoryBuffer answer;
if (!answer.RestApiPut(uri, body, bodySize, applyPlugins))
{
return false;
}
else
{
if (!answer.IsEmpty()) // i.e, on a PUT to metadata/..., orthanc returns an empty response
{
answer.ToJson(result);
}
return true;
}
}
bool RestApiPut(Json::Value& result,
const std::string& uri,
const Json::Value& body,
bool applyPlugins)
{
std::string s;
WriteFastJson(s, body);
return RestApiPut(result, uri, s, applyPlugins);
}
bool RestApiDelete(const std::string& uri,
bool applyPlugins)
{
OrthancPluginErrorCode error;
if (applyPlugins)
{
error = OrthancPluginRestApiDeleteAfterPlugins(GetGlobalContext(), uri.c_str());
}
else
{
error = OrthancPluginRestApiDelete(GetGlobalContext(), uri.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);
}
}
void ReportMinimalOrthancVersion(unsigned int major,
unsigned int minor,
unsigned int revision)
{
ORTHANC_PLUGINS_LOG_ERROR("Your version of the Orthanc core (" +
std::string(GetGlobalContext()->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)");
}
bool CheckMinimalVersion(const char* version,
unsigned int major,
unsigned int minor,
unsigned int revision)
{
if (!strcmp(version, "mainline"))
{
// Assume compatibility with the mainline
return true;
}
#ifdef _MSC_VER
#define ORTHANC_SCANF sscanf_s
#else
#define ORTHANC_SCANF sscanf
#endif
// Parse the version
int aa, bb, cc = 0;
if ((ORTHANC_SCANF(version, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 &&
ORTHANC_SCANF(version, "%4d.%4d", &aa, &bb) != 2) ||
aa < 0 ||
bb < 0 ||
cc < 0)
{
return 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;
}
}
bool CheckMinimalOrthancVersion(unsigned int major,
unsigned int minor,
unsigned int revision)
{
if (!HasGlobalContext())
{
ORTHANC_PLUGINS_LOG_ERROR("Bad Orthanc context in the plugin");
return false;
}
return CheckMinimalVersion(GetGlobalContext()->orthancVersion,
major, minor, revision);
}
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
const char* AutodetectMimeType(const std::string& path)
{
const char* mime = OrthancPluginAutodetectMimeType(GetGlobalContext(), path.c_str());
if (mime == NULL)
{
// Should never happen, just for safety
return "application/octet-stream";
}
else
{
return mime;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_PEERS == 1
size_t OrthancPeers::GetPeerIndex(const std::string& name) const
{
size_t index;
if (LookupName(index, name))
{
return index;
}
else
{
ORTHANC_PLUGINS_LOG_ERROR("Inexistent peer: " + name);
ORTHANC_PLUGINS_THROW_EXCEPTION(UnknownResource);
}
}
OrthancPeers::OrthancPeers() :
peers_(NULL),
timeout_(0)
{
peers_ = OrthancPluginGetPeers(GetGlobalContext());
if (peers_ == NULL)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
}
uint32_t count = OrthancPluginGetPeersCount(GetGlobalContext(), peers_);
for (uint32_t i = 0; i < count; i++)
{
const char* name = OrthancPluginGetPeerName(GetGlobalContext(), peers_, i);
if (name == NULL)
{
OrthancPluginFreePeers(GetGlobalContext(), peers_);
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
}
index_[name] = i;
}
}
OrthancPeers::~OrthancPeers()
{
if (peers_ != NULL)
{
OrthancPluginFreePeers(GetGlobalContext(), peers_);
}
}
bool OrthancPeers::LookupName(size_t& target,
const std::string& name) const
{
Index::const_iterator found = index_.find(name);
if (found == index_.end())
{
return false;
}
else
{
target = found->second;
return true;
}
}
std::string OrthancPeers::GetPeerName(size_t index) const
{
if (index >= index_.size())
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
}
else
{
const char* s = OrthancPluginGetPeerName(GetGlobalContext(), peers_, static_cast<uint32_t>(index));
if (s == NULL)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
}
else
{
return s;
}
}
}
std::string OrthancPeers::GetPeerUrl(size_t index) const
{
if (index >= index_.size())
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
}
else
{
const char* s = OrthancPluginGetPeerUrl(GetGlobalContext(), peers_, static_cast<uint32_t>(index));
if (s == NULL)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
}
else
{
return s;
}
}
}
std::string OrthancPeers::GetPeerUrl(const std::string& name) const
{
return GetPeerUrl(GetPeerIndex(name));
}
bool OrthancPeers::LookupUserProperty(std::string& value,
size_t index,
const std::string& key) const
{
if (index >= index_.size())
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
}
else
{
const char* s = OrthancPluginGetPeerUserProperty(GetGlobalContext(), peers_, static_cast<uint32_t>(index), key.c_str());
if (s == NULL)
{
return false;
}
else
{
value.assign(s);
return true;
}
}
}
bool OrthancPeers::LookupUserProperty(std::string& value,
const std::string& peer,
const std::string& key) const
{
return LookupUserProperty(value, GetPeerIndex(peer), key);
}
bool OrthancPeers::DoGet(MemoryBuffer& target,
size_t index,
const std::string& uri,
const HttpHeaders& headers) const
{
if (index >= index_.size())
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
}
OrthancPlugins::MemoryBuffer answer;
uint16_t status;
PluginHttpHeaders pluginHeaders(headers);
OrthancPluginErrorCode code = OrthancPluginCallPeerApi
(GetGlobalContext(), *answer, NULL, &status, peers_,
static_cast<uint32_t>(index), OrthancPluginHttpMethod_Get, uri.c_str(),
pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), NULL, 0, timeout_);
if (code == OrthancPluginErrorCode_Success)
{
target.Swap(answer);
return (status == 200);
}
else
{
return false;
}
}
bool OrthancPeers::DoGet(MemoryBuffer& target,
const std::string& name,
const std::string& uri,
const HttpHeaders& headers) const
{
size_t index;
return (LookupName(index, name) &&
DoGet(target, index, uri, headers));
}
bool OrthancPeers::DoGet(Json::Value& target,
size_t index,
const std::string& uri,
const HttpHeaders& headers) const
{
MemoryBuffer buffer;
if (DoGet(buffer, index, uri, headers))
{
buffer.ToJson(target);
return true;
}
else
{
return false;
}
}
bool OrthancPeers::DoGet(Json::Value& target,
const std::string& name,
const std::string& uri,
const HttpHeaders& headers) const
{
MemoryBuffer buffer;
if (DoGet(buffer, name, uri, headers))
{
buffer.ToJson(target);
return true;
}
else
{
return false;
}
}
bool OrthancPeers::DoPost(MemoryBuffer& target,
const std::string& name,
const std::string& uri,
const std::string& body,
const HttpHeaders& headers) const
{
size_t index;
return (LookupName(index, name) &&
DoPost(target, index, uri, body, headers));
}
bool OrthancPeers::DoPost(Json::Value& target,
size_t index,
const std::string& uri,
const std::string& body,
const HttpHeaders& headers,
unsigned int timeout) const
{
MemoryBuffer buffer;
if (DoPost(buffer, index, uri, body, headers, timeout))
{
buffer.ToJson(target);
return true;
}
else
{
return false;
}
}
bool OrthancPeers::DoPost(Json::Value& target,
size_t index,
const std::string& uri,
const std::string& body,
const HttpHeaders& headers) const
{
MemoryBuffer buffer;
if (DoPost(buffer, index, uri, body, headers))
{
buffer.ToJson(target);
return true;
}
else
{
return false;
}
}
bool OrthancPeers::DoPost(Json::Value& target,
const std::string& name,
const std::string& uri,
const std::string& body,
const HttpHeaders& headers) const
{
MemoryBuffer buffer;
if (DoPost(buffer, name, uri, body, headers))
{
buffer.ToJson(target);
return true;
}
else
{
return false;
}
}
bool OrthancPeers::DoPost(MemoryBuffer& target,
size_t index,
const std::string& uri,
const std::string& body,
const HttpHeaders& headers) const
{
return DoPost(target, index, uri, body, headers, timeout_);
}
bool OrthancPeers::DoPost(MemoryBuffer& target,
size_t index,
const std::string& uri,
const std::string& body,
const HttpHeaders& headers,
unsigned int timeout) const
{
if (index >= index_.size())
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
}
if (body.size() > 0xffffffffu)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot handle body size > 4GB");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
OrthancPlugins::MemoryBuffer answer;
uint16_t status;
PluginHttpHeaders pluginHeaders(headers);
OrthancPluginErrorCode code = OrthancPluginCallPeerApi
(GetGlobalContext(), *answer, NULL, &status, peers_,
static_cast<uint32_t>(index), OrthancPluginHttpMethod_Post, uri.c_str(),
pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), body.empty() ? NULL : body.c_str(), body.size(), timeout);
if (code == OrthancPluginErrorCode_Success)
{
target.Swap(answer);
return (status == 200);
}
else
{
return false;
}
}
bool OrthancPeers::DoPut(size_t index,
const std::string& uri,
const std::string& body,
const HttpHeaders& headers) const
{
if (index >= index_.size())
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
}
if (body.size() > 0xffffffffu)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot handle body size > 4GB");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
OrthancPlugins::MemoryBuffer answer;
uint16_t status;
PluginHttpHeaders pluginHeaders(headers);
OrthancPluginErrorCode code = OrthancPluginCallPeerApi
(GetGlobalContext(), *answer, NULL, &status, peers_,
static_cast<uint32_t>(index), OrthancPluginHttpMethod_Put, uri.c_str(),
pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), body.empty() ? NULL : body.c_str(), body.size(), timeout_);
if (code == OrthancPluginErrorCode_Success)
{
return (status == 200);
}
else
{
return false;
}
}
bool OrthancPeers::DoPut(const std::string& name,
const std::string& uri,
const std::string& body,
const HttpHeaders& headers) const
{
size_t index;
return (LookupName(index, name) &&
DoPut(index, uri, body, headers));
}
bool OrthancPeers::DoDelete(size_t index,
const std::string& uri,
const HttpHeaders& headers) const
{
if (index >= index_.size())
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
}
OrthancPlugins::MemoryBuffer answer;
uint16_t status;
PluginHttpHeaders pluginHeaders(headers);
OrthancPluginErrorCode code = OrthancPluginCallPeerApi
(GetGlobalContext(), *answer, NULL, &status, peers_,
static_cast<uint32_t>(index), OrthancPluginHttpMethod_Delete, uri.c_str(),
pluginHeaders.GetSize(), pluginHeaders.GetKeys(), pluginHeaders.GetValues(), NULL, 0, timeout_);
if (code == OrthancPluginErrorCode_Success)
{
return (status == 200);
}
else
{
return false;
}
}
bool OrthancPeers::DoDelete(const std::string& name,
const std::string& uri,
const HttpHeaders& headers) const
{
size_t index;
return (LookupName(index, name) &&
DoDelete(index, uri, headers));
}
#endif
/******************************************************************
** JOBS
******************************************************************/
#if HAS_ORTHANC_PLUGIN_JOB == 1
void OrthancJob::CallbackFinalize(void* job)
{
if (job != NULL)
{
delete reinterpret_cast<OrthancJob*>(job);
}
}
float OrthancJob::CallbackGetProgress(void* job)
{
assert(job != NULL);
try
{
return reinterpret_cast<OrthancJob*>(job)->progress_;
}
catch (...)
{
return 0;
}
}
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3)
static OrthancPluginErrorCode CopyStringToMemoryBuffer(OrthancPluginMemoryBuffer* target,
const std::string& source)
{
if (OrthancPluginCreateMemoryBuffer(globalContext_, target, source.size()) != OrthancPluginErrorCode_Success)
{
return OrthancPluginErrorCode_NotEnoughMemory;
}
else
{
if (!source.empty())
{
memcpy(target->data, source.c_str(), source.size());
}
return OrthancPluginErrorCode_Success;
}
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3)
OrthancPluginErrorCode OrthancJob::CallbackGetContent(OrthancPluginMemoryBuffer* target,
void* job)
{
assert(job != NULL);
OrthancJob& that = *reinterpret_cast<OrthancJob*>(job);
return CopyStringToMemoryBuffer(target, that.content_);
}
#else
const char* OrthancJob::CallbackGetContent(void* job)
{
assert(job != NULL);
try
{
return reinterpret_cast<OrthancJob*>(job)->content_.c_str();
}
catch (...)
{
return 0;
}
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3)
int32_t OrthancJob::CallbackGetSerialized(OrthancPluginMemoryBuffer* target,
void* job)
{
assert(job != NULL);
OrthancJob& that = *reinterpret_cast<OrthancJob*>(job);
if (that.hasSerialized_)
{
if (CopyStringToMemoryBuffer(target, that.serialized_) == OrthancPluginErrorCode_Success)
{
return 1;
}
else
{
return -1;
}
}
else
{
return 0;
}
}
#else
const char* OrthancJob::CallbackGetSerialized(void* job)
{
assert(job != NULL);
try
{
const OrthancJob& tmp = *reinterpret_cast<OrthancJob*>(job);
if (tmp.hasSerialized_)
{
return tmp.serialized_.c_str();
}
else
{
return NULL;
}
}
catch (...)
{
return 0;
}
}
#endif
OrthancPluginJobStepStatus OrthancJob::CallbackStep(void* job)
{
assert(job != NULL);
try
{
return reinterpret_cast<OrthancJob*>(job)->Step();
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&)
{
return OrthancPluginJobStepStatus_Failure;
}
catch (...)
{
return OrthancPluginJobStepStatus_Failure;
}
}
OrthancPluginErrorCode OrthancJob::CallbackStop(void* job,
OrthancPluginJobStopReason reason)
{
assert(job != NULL);
try
{
reinterpret_cast<OrthancJob*>(job)->Stop(reason);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
OrthancPluginErrorCode OrthancJob::CallbackReset(void* job)
{
assert(job != NULL);
try
{
reinterpret_cast<OrthancJob*>(job)->Reset();
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
void OrthancJob::ClearContent()
{
Json::Value empty = Json::objectValue;
UpdateContent(empty);
}
void OrthancJob::UpdateContent(const Json::Value& content)
{
if (content.type() != Json::objectValue)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
}
else
{
WriteFastJson(content_, content);
}
}
void OrthancJob::ClearSerialized()
{
hasSerialized_ = false;
serialized_.clear();
}
void OrthancJob::UpdateSerialized(const Json::Value& serialized)
{
if (serialized.type() != Json::objectValue)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
}
else
{
WriteFastJson(serialized_, serialized);
hasSerialized_ = true;
}
}
void OrthancJob::UpdateProgress(float progress)
{
if (progress < 0 ||
progress > 1)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
}
progress_ = progress;
}
OrthancJob::OrthancJob(const std::string& jobType) :
jobType_(jobType),
progress_(0)
{
ClearContent();
ClearSerialized();
}
OrthancPluginJob* OrthancJob::Create(OrthancJob* job)
{
if (job == NULL)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
}
OrthancPluginJob* orthanc =
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 11, 3)
OrthancPluginCreateJob2
#else
OrthancPluginCreateJob
#endif
(GetGlobalContext(), job, CallbackFinalize, job->jobType_.c_str(),
CallbackGetProgress, CallbackGetContent, CallbackGetSerialized,
CallbackStep, CallbackStop, CallbackReset);
if (orthanc == NULL)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
}
else
{
return orthanc;
}
}
std::string OrthancJob::Submit(OrthancJob* job,
int priority)
{
if (job == NULL)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
}
OrthancPluginJob* orthanc = Create(job);
char* id = OrthancPluginSubmitJob(GetGlobalContext(), orthanc, priority);
if (id == NULL)
{
ORTHANC_PLUGINS_LOG_ERROR("Plugin cannot submit job");
OrthancPluginFreeJob(GetGlobalContext(), orthanc);
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
}
else
{
std::string tmp(id);
tmp.assign(id);
OrthancPluginFreeString(GetGlobalContext(), id);
return tmp;
}
}
void OrthancJob::SubmitAndWait(Json::Value& result,
OrthancJob* job /* takes ownership */,
int priority)
{
std::string id = Submit(job, priority);
for (;;)
{
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
Json::Value status;
if (!RestApiGet(status, "/jobs/" + id, false) ||
!status.isMember("State") ||
status["State"].type() != Json::stringValue)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InexistentItem);
}
const std::string state = status["State"].asString();
if (state == "Success")
{
if (status.isMember("Content"))
{
result = status["Content"];
}
else
{
result = Json::objectValue;
}
return;
}
else if (state == "Running")
{
continue;
}
else if (!status.isMember("ErrorCode") ||
status["ErrorCode"].type() != Json::intValue)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InternalError);
}
else
{
if (!status.isMember("ErrorDescription") ||
status["ErrorDescription"].type() != Json::stringValue)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());
}
else
{
#if HAS_ORTHANC_EXCEPTION == 1
throw Orthanc::OrthancException(static_cast<Orthanc::ErrorCode>(status["ErrorCode"].asInt()),
status["ErrorDescription"].asString());
#else
ORTHANC_PLUGINS_LOG_ERROR("Exception while executing the job: " + status["ErrorDescription"].asString());
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());
#endif
}
}
}
}
void OrthancJob::SubmitFromRestApiPost(OrthancPluginRestOutput* output,
const Json::Value& body,
OrthancJob* job)
{
static const char* KEY_SYNCHRONOUS = "Synchronous";
static const char* KEY_ASYNCHRONOUS = "Asynchronous";
static const char* KEY_PRIORITY = "Priority";
boost::movelib::unique_ptr<OrthancJob> protection(job);
if (body.type() != Json::objectValue)
{
#if HAS_ORTHANC_EXCEPTION == 1
throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
"Expected a JSON object in the body");
#else
ORTHANC_PLUGINS_LOG_ERROR("Expected a JSON object in the body");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
#endif
}
bool synchronous = true;
if (body.isMember(KEY_SYNCHRONOUS))
{
if (body[KEY_SYNCHRONOUS].type() != Json::booleanValue)
{
#if HAS_ORTHANC_EXCEPTION == 1
throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
"Option \"" + std::string(KEY_SYNCHRONOUS) +
"\" must be Boolean");
#else
ORTHANC_PLUGINS_LOG_ERROR("Option \"" + std::string(KEY_SYNCHRONOUS) + "\" must be Boolean");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
#endif
}
else
{
synchronous = body[KEY_SYNCHRONOUS].asBool();
}
}
if (body.isMember(KEY_ASYNCHRONOUS))
{
if (body[KEY_ASYNCHRONOUS].type() != Json::booleanValue)
{
#if HAS_ORTHANC_EXCEPTION == 1
throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
"Option \"" + std::string(KEY_ASYNCHRONOUS) +
"\" must be Boolean");
#else
ORTHANC_PLUGINS_LOG_ERROR("Option \"" + std::string(KEY_ASYNCHRONOUS) + "\" must be Boolean");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
#endif
}
else
{
synchronous = !body[KEY_ASYNCHRONOUS].asBool();
}
}
int priority = 0;
if (body.isMember(KEY_PRIORITY))
{
if (body[KEY_PRIORITY].type() != Json::intValue)
{
#if HAS_ORTHANC_EXCEPTION == 1
throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
"Option \"" + std::string(KEY_PRIORITY) +
"\" must be an integer");
#else
ORTHANC_PLUGINS_LOG_ERROR("Option \"" + std::string(KEY_PRIORITY) + "\" must be an integer");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
#endif
}
else
{
priority = !body[KEY_PRIORITY].asInt();
}
}
Json::Value result;
if (synchronous)
{
OrthancPlugins::OrthancJob::SubmitAndWait(result, protection.release(), priority);
}
else
{
std::string id = OrthancPlugins::OrthancJob::Submit(protection.release(), priority);
result = Json::objectValue;
result["ID"] = id;
result["Path"] = "/jobs/" + id;
}
std::string s = result.toStyledString();
OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(),
s.size(), "application/json");
}
#endif
/******************************************************************
** METRICS
******************************************************************/
#if HAS_ORTHANC_PLUGIN_METRICS == 1
MetricsTimer::MetricsTimer(const char* name) :
name_(name)
{
start_ = boost::posix_time::microsec_clock::universal_time();
}
MetricsTimer::~MetricsTimer()
{
const boost::posix_time::ptime stop = boost::posix_time::microsec_clock::universal_time();
const boost::posix_time::time_duration diff = stop - start_;
OrthancPluginSetMetricsValue(GetGlobalContext(), name_.c_str(), static_cast<float>(diff.total_milliseconds()),
OrthancPluginMetricsType_Timer);
}
#endif
/******************************************************************
** HTTP CLIENT
******************************************************************/
#if HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1
class HttpClient::RequestBodyWrapper : public boost::noncopyable
{
private:
static RequestBodyWrapper& GetObject(void* body)
{
assert(body != NULL);
return *reinterpret_cast<RequestBodyWrapper*>(body);
}
IRequestBody& body_;
bool done_;
std::string chunk_;
public:
RequestBodyWrapper(IRequestBody& body) :
body_(body),
done_(false)
{
}
static uint8_t IsDone(void* body)
{
return GetObject(body).done_;
}
static const void* GetChunkData(void* body)
{
return GetObject(body).chunk_.c_str();
}
static uint32_t GetChunkSize(void* body)
{
return static_cast<uint32_t>(GetObject(body).chunk_.size());
}
static OrthancPluginErrorCode Next(void* body)
{
RequestBodyWrapper& that = GetObject(body);
if (that.done_)
{
return OrthancPluginErrorCode_BadSequenceOfCalls;
}
else
{
try
{
that.done_ = !that.body_.ReadNextChunk(that.chunk_);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
}
};
#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
static OrthancPluginErrorCode AnswerAddHeaderCallback(void* answer,
const char* key,
const char* value)
{
assert(answer != NULL && key != NULL && value != NULL);
try
{
reinterpret_cast<HttpClient::IAnswer*>(answer)->AddHeader(key, value);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
static OrthancPluginErrorCode AnswerAddChunkCallback(void* answer,
const void* data,
uint32_t size)
{
assert(answer != NULL);
try
{
reinterpret_cast<HttpClient::IAnswer*>(answer)->AddChunk(data, size);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
#endif
HttpClient::HttpClient() :
httpStatus_(0),
method_(OrthancPluginHttpMethod_Get),
timeout_(0),
pkcs11_(false),
chunkedBody_(NULL),
allowChunkedTransfers_(true)
{
}
void HttpClient::AddHeaders(const HttpHeaders& headers)
{
for (HttpHeaders::const_iterator it = headers.begin();
it != headers.end(); ++it)
{
headers_[it->first] = it->second;
}
}
void HttpClient::SetCredentials(const std::string& username,
const std::string& password)
{
username_ = username;
password_ = password;
}
void HttpClient::ClearCredentials()
{
username_.clear();
password_.clear();
}
void HttpClient::SetCertificate(const std::string& certificateFile,
const std::string& keyFile,
const std::string& keyPassword)
{
certificateFile_ = certificateFile;
certificateKeyFile_ = keyFile;
certificateKeyPassword_ = keyPassword;
}
void HttpClient::ClearCertificate()
{
certificateFile_.clear();
certificateKeyFile_.clear();
certificateKeyPassword_.clear();
}
void HttpClient::ClearBody()
{
fullBody_.clear();
chunkedBody_ = NULL;
}
void HttpClient::SwapBody(std::string& body)
{
fullBody_.swap(body);
chunkedBody_ = NULL;
}
void HttpClient::SetBody(const std::string& body)
{
fullBody_ = body;
chunkedBody_ = NULL;
}
void HttpClient::SetBody(IRequestBody& body)
{
fullBody_.clear();
chunkedBody_ = &body;
}
namespace
{
class HeadersWrapper : public boost::noncopyable
{
private:
std::vector<const char*> headersKeys_;
std::vector<const char*> headersValues_;
public:
HeadersWrapper(const HttpHeaders& headers)
{
headersKeys_.reserve(headers.size());
headersValues_.reserve(headers.size());
for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); ++it)
{
headersKeys_.push_back(it->first.c_str());
headersValues_.push_back(it->second.c_str());
}
}
void AddStaticString(const char* key,
const char* value)
{
headersKeys_.push_back(key);
headersValues_.push_back(value);
}
uint32_t GetCount() const
{
return headersKeys_.size();
}
const char* const* GetKeys() const
{
return headersKeys_.empty() ? NULL : &headersKeys_[0];
}
const char* const* GetValues() const
{
return headersValues_.empty() ? NULL : &headersValues_[0];
}
};
class MemoryRequestBody : public HttpClient::IRequestBody
{
private:
std::string body_;
bool done_;
public:
MemoryRequestBody(const std::string& body) :
body_(body),
done_(false)
{
if (body_.empty())
{
done_ = true;
}
}
virtual bool ReadNextChunk(std::string& chunk)
{
if (done_)
{
return false;
}
else
{
chunk.swap(body_);
done_ = true;
return true;
}
}
};
// This class mimics Orthanc::ChunkedBuffer
class ChunkedBuffer : public boost::noncopyable
{
private:
typedef std::list<std::string*> Content;
Content content_;
size_t size_;
public:
ChunkedBuffer() :
size_(0)
{
}
~ChunkedBuffer()
{
Clear();
}
void Clear()
{
for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
{
assert(*it != NULL);
delete *it;
}
size_ = 0;
content_.clear();
}
/**
* Since Orthanc 1.9.3, this function also clears the content of
* the ChunkedBuffer in order to mimic the behavior of the
* original class "Orthanc::ChunkedBuffer". This prevents the
* forgetting of calling "Clear()" in order to reduce memory
* consumption.
**/
void Flatten(std::string& target)
{
target.resize(size_);
size_t pos = 0;
for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it)
{
assert(*it != NULL);
size_t s = (*it)->size();
if (s != 0)
{
memcpy(&target[pos], (*it)->c_str(), s);
pos += s;
}
delete *it;
}
assert(pos == target.size());
size_ = 0;
content_.clear();
}
void AddChunk(const void* data,
size_t size)
{
content_.push_back(new std::string(reinterpret_cast<const char*>(data), size));
size_ += size;
}
void AddChunk(const std::string& chunk)
{
content_.push_back(new std::string(chunk));
size_ += chunk.size();
}
};
#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
class MemoryAnswer : public HttpClient::IAnswer
{
private:
HttpHeaders headers_;
ChunkedBuffer body_;
public:
const HttpHeaders& GetHeaders() const
{
return headers_;
}
ChunkedBuffer& GetBody()
{
return body_;
}
virtual void AddHeader(const std::string& key,
const std::string& value)
{
headers_[key] = value;
}
virtual void AddChunk(const void* data,
size_t size)
{
body_.AddChunk(data, size);
}
};
#endif
}
#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
void HttpClient::ExecuteWithStream(uint16_t& httpStatus,
IAnswer& answer,
IRequestBody& body) const
{
HeadersWrapper h(headers_);
if (method_ == OrthancPluginHttpMethod_Post ||
method_ == OrthancPluginHttpMethod_Put)
{
// Automatically set the "Transfer-Encoding" header if absent
bool found = false;
for (HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); ++it)
{
if (boost::iequals(it->first, "Transfer-Encoding"))
{
found = true;
break;
}
}
if (!found)
{
h.AddStaticString("Transfer-Encoding", "chunked");
}
}
RequestBodyWrapper request(body);
OrthancPluginErrorCode error = OrthancPluginChunkedHttpClient(
GetGlobalContext(),
&answer,
AnswerAddChunkCallback,
AnswerAddHeaderCallback,
&httpStatus,
method_,
url_.c_str(),
h.GetCount(),
h.GetKeys(),
h.GetValues(),
&request,
RequestBodyWrapper::IsDone,
RequestBodyWrapper::GetChunkData,
RequestBodyWrapper::GetChunkSize,
RequestBodyWrapper::Next,
username_.empty() ? NULL : username_.c_str(),
password_.empty() ? NULL : password_.c_str(),
timeout_,
certificateFile_.empty() ? NULL : certificateFile_.c_str(),
certificateFile_.empty() ? NULL : certificateKeyFile_.c_str(),
certificateFile_.empty() ? NULL : certificateKeyPassword_.c_str(),
pkcs11_ ? 1 : 0);
if (error != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
}
}
#endif
static void DecodeHttpHeaders(HttpHeaders& target,
const MemoryBuffer& source)
{
Json::Value v;
source.ToJson(v);
if (v.type() != Json::objectValue)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
Json::Value::Members members = v.getMemberNames();
target.clear();
for (size_t i = 0; i < members.size(); i++)
{
const Json::Value& h = v[members[i]];
if (h.type() != Json::stringValue)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
else
{
target[members[i]] = h.asString();
}
}
}
void HttpClient::ExecuteWithoutStream(uint16_t& httpStatus,
HttpHeaders& answerHeaders,
std::string& answerBody,
const std::string& body) const
{
HeadersWrapper headers(headers_);
MemoryBuffer answerBodyBuffer, answerHeadersBuffer;
if (body.size() > 0xffffffffu)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot handle body size > 4GB");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
OrthancPluginErrorCode error = OrthancPluginHttpClient(
GetGlobalContext(),
*answerBodyBuffer,
*answerHeadersBuffer,
&httpStatus,
method_,
url_.c_str(),
headers.GetCount(),
headers.GetKeys(),
headers.GetValues(),
body.empty() ? NULL : body.c_str(),
body.size(),
username_.empty() ? NULL : username_.c_str(),
password_.empty() ? NULL : password_.c_str(),
timeout_,
certificateFile_.empty() ? NULL : certificateFile_.c_str(),
certificateFile_.empty() ? NULL : certificateKeyFile_.c_str(),
certificateFile_.empty() ? NULL : certificateKeyPassword_.c_str(),
pkcs11_ ? 1 : 0);
if (error != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
}
DecodeHttpHeaders(answerHeaders, answerHeadersBuffer);
answerBodyBuffer.ToString(answerBody);
}
void HttpClient::Execute(IAnswer& answer)
{
#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
if (allowChunkedTransfers_)
{
if (chunkedBody_ != NULL)
{
ExecuteWithStream(httpStatus_, answer, *chunkedBody_);
}
else
{
MemoryRequestBody wrapper(fullBody_);
ExecuteWithStream(httpStatus_, answer, wrapper);
}
return;
}
#endif
// Compatibility mode for Orthanc SDK <= 1.5.6 or if chunked
// transfers are disabled. This results in higher memory usage
// (all chunks from the answer body are sent at once)
HttpHeaders answerHeaders;
std::string answerBody;
Execute(answerHeaders, answerBody);
for (HttpHeaders::const_iterator it = answerHeaders.begin();
it != answerHeaders.end(); ++it)
{
answer.AddHeader(it->first, it->second);
}
if (!answerBody.empty())
{
answer.AddChunk(answerBody.c_str(), answerBody.size());
}
}
void HttpClient::Execute(HttpHeaders& answerHeaders /* out */,
std::string& answerBody /* out */)
{
#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
if (allowChunkedTransfers_)
{
MemoryAnswer answer;
Execute(answer);
answerHeaders = answer.GetHeaders();
answer.GetBody().Flatten(answerBody);
return;
}
#endif
// Compatibility mode for Orthanc SDK <= 1.5.6 or if chunked
// transfers are disabled. This results in higher memory usage
// (all chunks from the request body are sent at once)
if (chunkedBody_ != NULL)
{
ChunkedBuffer buffer;
std::string chunk;
while (chunkedBody_->ReadNextChunk(chunk))
{
buffer.AddChunk(chunk);
}
std::string body;
buffer.Flatten(body);
ExecuteWithoutStream(httpStatus_, answerHeaders, answerBody, body);
}
else
{
ExecuteWithoutStream(httpStatus_, answerHeaders, answerBody, fullBody_);
}
}
void HttpClient::Execute(HttpHeaders& answerHeaders /* out */,
Json::Value& answerBody /* out */)
{
std::string body;
Execute(answerHeaders, body);
if (!ReadJson(answerBody, body))
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot convert HTTP answer body to JSON");
ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
}
}
void HttpClient::Execute()
{
HttpHeaders answerHeaders;
std::string body;
Execute(answerHeaders, body);
}
#endif /* HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1 */
/******************************************************************
** CHUNKED HTTP SERVER
******************************************************************/
namespace Internals
{
void NullRestCallback(OrthancPluginRestOutput* output,
const char* url,
const OrthancPluginHttpRequest* request)
{
}
IChunkedRequestReader *NullChunkedRestCallback(const char* url,
const OrthancPluginHttpRequest* request)
{
return NULL;
}
#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
OrthancPluginErrorCode ChunkedRequestReaderAddChunk(
OrthancPluginServerChunkedRequestReader* reader,
const void* data,
uint32_t size)
{
try
{
if (reader == NULL)
{
return OrthancPluginErrorCode_InternalError;
}
reinterpret_cast<IChunkedRequestReader*>(reader)->AddChunk(data, size);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (boost::bad_lexical_cast&)
{
return OrthancPluginErrorCode_BadFileFormat;
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
OrthancPluginErrorCode ChunkedRequestReaderExecute(
OrthancPluginServerChunkedRequestReader* reader,
OrthancPluginRestOutput* output)
{
try
{
if (reader == NULL)
{
return OrthancPluginErrorCode_InternalError;
}
reinterpret_cast<IChunkedRequestReader*>(reader)->Execute(output);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (boost::bad_lexical_cast&)
{
return OrthancPluginErrorCode_BadFileFormat;
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
void ChunkedRequestReaderFinalize(
OrthancPluginServerChunkedRequestReader* reader)
{
if (reader != NULL)
{
delete reinterpret_cast<IChunkedRequestReader*>(reader);
}
}
#else
OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output,
const char* url,
const OrthancPluginHttpRequest* request,
RestCallback GetHandler,
ChunkedRestCallback PostHandler,
RestCallback DeleteHandler,
ChunkedRestCallback PutHandler)
{
try
{
std::string allowed;
if (GetHandler != Internals::NullRestCallback)
{
allowed += "GET";
}
if (PostHandler != Internals::NullChunkedRestCallback)
{
if (!allowed.empty())
{
allowed += ",";
}
allowed += "POST";
}
if (DeleteHandler != Internals::NullRestCallback)
{
if (!allowed.empty())
{
allowed += ",";
}
allowed += "DELETE";
}
if (PutHandler != Internals::NullChunkedRestCallback)
{
if (!allowed.empty())
{
allowed += ",";
}
allowed += "PUT";
}
switch (request->method)
{
case OrthancPluginHttpMethod_Get:
if (GetHandler == Internals::NullRestCallback)
{
OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
}
else
{
GetHandler(output, url, request);
}
break;
case OrthancPluginHttpMethod_Post:
if (PostHandler == Internals::NullChunkedRestCallback)
{
OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
}
else
{
boost::movelib::unique_ptr<IChunkedRequestReader> reader(PostHandler(url, request));
if (reader.get() == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
}
else
{
reader->AddChunk(request->body, request->bodySize);
reader->Execute(output);
}
}
break;
case OrthancPluginHttpMethod_Delete:
if (DeleteHandler == Internals::NullRestCallback)
{
OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
}
else
{
DeleteHandler(output, url, request);
}
break;
case OrthancPluginHttpMethod_Put:
if (PutHandler == Internals::NullChunkedRestCallback)
{
OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
}
else
{
boost::movelib::unique_ptr<IChunkedRequestReader> reader(PutHandler(url, request));
if (reader.get() == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
}
else
{
reader->AddChunk(request->body, request->bodySize);
reader->Execute(output);
}
}
break;
default:
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
#if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1
if (HasGlobalContext() &&
e.HasDetails())
{
// The "false" instructs Orthanc not to log the detailed
// error message. This is to avoid duplicating the details,
// because "OrthancException" already does it on construction.
OrthancPluginSetHttpErrorDetails
(GetGlobalContext(), output, e.GetDetails(), false);
}
#endif
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (boost::bad_lexical_cast&)
{
return OrthancPluginErrorCode_BadFileFormat;
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
#endif
}
#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
OrthancPluginErrorCode IStorageCommitmentScpHandler::Lookup(
OrthancPluginStorageCommitmentFailureReason* target,
void* rawHandler,
const char* sopClassUid,
const char* sopInstanceUid)
{
assert(target != NULL &&
rawHandler != NULL);
try
{
IStorageCommitmentScpHandler& handler = *reinterpret_cast<IStorageCommitmentScpHandler*>(rawHandler);
*target = handler.Lookup(sopClassUid, sopInstanceUid);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
void IStorageCommitmentScpHandler::Destructor(void* rawHandler)
{
assert(rawHandler != NULL);
delete reinterpret_cast<IStorageCommitmentScpHandler*>(rawHandler);
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
DicomInstance::DicomInstance(const OrthancPluginDicomInstance* instance) :
toFree_(false),
instance_(instance)
{
}
#else
DicomInstance::DicomInstance(OrthancPluginDicomInstance* instance) :
toFree_(false),
instance_(instance)
{
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
DicomInstance::DicomInstance(const void* buffer,
size_t size) :
toFree_(true),
instance_(OrthancPluginCreateDicomInstance(GetGlobalContext(), buffer, size))
{
if (instance_ == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(NullPointer);
}
}
#endif
DicomInstance::~DicomInstance()
{
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
if (toFree_ &&
instance_ != NULL)
{
OrthancPluginFreeDicomInstance(
GetGlobalContext(), const_cast<OrthancPluginDicomInstance*>(instance_));
}
#endif
}
std::string DicomInstance::GetRemoteAet() const
{
const char* s = OrthancPluginGetInstanceRemoteAet(GetGlobalContext(), instance_);
if (s == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
}
else
{
return std::string(s);
}
}
void DicomInstance::GetJson(Json::Value& target) const
{
OrthancString s;
s.Assign(OrthancPluginGetInstanceJson(GetGlobalContext(), instance_));
s.ToJson(target);
}
void DicomInstance::GetSimplifiedJson(Json::Value& target) const
{
OrthancString s;
s.Assign(OrthancPluginGetInstanceSimplifiedJson(GetGlobalContext(), instance_));
s.ToJson(target);
}
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
std::string DicomInstance::GetTransferSyntaxUid() const
{
OrthancString s;
s.Assign(OrthancPluginGetInstanceTransferSyntaxUid(GetGlobalContext(), instance_));
std::string result;
s.ToString(result);
return result;
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
bool DicomInstance::HasPixelData() const
{
int32_t result = OrthancPluginHasInstancePixelData(GetGlobalContext(), instance_);
if (result < 0)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
}
else
{
return (result != 0);
}
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
void DicomInstance::GetRawFrame(std::string& target,
unsigned int frameIndex) const
{
MemoryBuffer buffer;
OrthancPluginErrorCode code = OrthancPluginGetInstanceRawFrame(
GetGlobalContext(), *buffer, instance_, frameIndex);
if (code == OrthancPluginErrorCode_Success)
{
buffer.ToString(target);
}
else
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
OrthancImage* DicomInstance::GetDecodedFrame(unsigned int frameIndex) const
{
OrthancPluginImage* image = OrthancPluginGetInstanceDecodedFrame(
GetGlobalContext(), instance_, frameIndex);
if (image == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
}
else
{
return new OrthancImage(image);
}
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
void DicomInstance::Serialize(std::string& target) const
{
MemoryBuffer buffer;
OrthancPluginErrorCode code = OrthancPluginSerializeDicomInstance(
GetGlobalContext(), *buffer, instance_);
if (code == OrthancPluginErrorCode_Success)
{
buffer.ToString(target);
}
else
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
DicomInstance* DicomInstance::Transcode(const void* buffer,
size_t size,
const std::string& transferSyntax)
{
OrthancPluginDicomInstance* instance = OrthancPluginTranscodeDicomInstance(
GetGlobalContext(), buffer, size, transferSyntax.c_str());
if (instance == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
}
else
{
boost::movelib::unique_ptr<DicomInstance> result(new DicomInstance(instance));
result->toFree_ = true;
return result.release();
}
}
#endif
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 1)
DicomInstance* DicomInstance::Load(const std::string& instanceId,
OrthancPluginLoadDicomInstanceMode mode)
{
OrthancPluginDicomInstance* instance = OrthancPluginLoadDicomInstance(
GetGlobalContext(), instanceId.c_str(), mode);
if (instance == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
}
else
{
boost::movelib::unique_ptr<DicomInstance> result(new DicomInstance(instance));
result->toFree_ = true;
return result.release();
}
}
#endif
#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
static std::vector<std::string> WebDavConvertPath(uint32_t pathSize,
const char* const* pathItems)
{
std::vector<std::string> result(pathSize);
for (uint32_t i = 0; i < pathSize; i++)
{
result[i] = pathItems[i];
}
return result;
}
#endif
#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
static OrthancPluginErrorCode WebDavIsExistingFolder(uint8_t* isExisting,
uint32_t pathSize,
const char* const* pathItems,
void* payload)
{
IWebDavCollection& that = *reinterpret_cast<IWebDavCollection*>(payload);
try
{
*isExisting = (that.IsExistingFolder(WebDavConvertPath(pathSize, pathItems)) ? 1 : 0);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
static OrthancPluginErrorCode WebDavListFolder(uint8_t* isExisting,
OrthancPluginWebDavCollection* collection,
OrthancPluginWebDavAddFile addFile,
OrthancPluginWebDavAddFolder addFolder,
uint32_t pathSize,
const char* const* pathItems,
void* payload)
{
IWebDavCollection& that = *reinterpret_cast<IWebDavCollection*>(payload);
try
{
std::list<IWebDavCollection::FileInfo> files;
std::list<IWebDavCollection::FolderInfo> subfolders;
if (!that.ListFolder(files, subfolders, WebDavConvertPath(pathSize, pathItems)))
{
*isExisting = 0;
}
else
{
*isExisting = 1;
for (std::list<IWebDavCollection::FileInfo>::const_iterator
it = files.begin(); it != files.end(); ++it)
{
OrthancPluginErrorCode code = addFile(
collection, it->GetName().c_str(), it->GetContentSize(),
it->GetMimeType().c_str(), it->GetDateTime().c_str());
if (code != OrthancPluginErrorCode_Success)
{
return code;
}
}
for (std::list<IWebDavCollection::FolderInfo>::const_iterator it =
subfolders.begin(); it != subfolders.end(); ++it)
{
OrthancPluginErrorCode code = addFolder(
collection, it->GetName().c_str(), it->GetDateTime().c_str());
if (code != OrthancPluginErrorCode_Success)
{
return code;
}
}
}
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_Plugin;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
static OrthancPluginErrorCode WebDavRetrieveFile(OrthancPluginWebDavCollection* collection,
OrthancPluginWebDavRetrieveFile retrieveFile,
uint32_t pathSize,
const char* const* pathItems,
void* payload)
{
IWebDavCollection& that = *reinterpret_cast<IWebDavCollection*>(payload);
try
{
std::string content, mime, dateTime;
if (that.GetFile(content, mime, dateTime, WebDavConvertPath(pathSize, pathItems)))
{
return retrieveFile(collection, content.empty() ? NULL : content.c_str(),
content.size(), mime.c_str(), dateTime.c_str());
}
else
{
// Inexisting file
return OrthancPluginErrorCode_Success;
}
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_InternalError;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
static OrthancPluginErrorCode WebDavStoreFileCallback(uint8_t* isReadOnly, /* out */
uint32_t pathSize,
const char* const* pathItems,
const void* data,
uint64_t size,
void* payload)
{
IWebDavCollection& that = *reinterpret_cast<IWebDavCollection*>(payload);
try
{
if (static_cast<uint64_t>(static_cast<size_t>(size)) != size)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory);
}
*isReadOnly = (that.StoreFile(WebDavConvertPath(pathSize, pathItems), data,
static_cast<size_t>(size)) ? 1 : 0);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_InternalError;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
static OrthancPluginErrorCode WebDavCreateFolderCallback(uint8_t* isReadOnly, /* out */
uint32_t pathSize,
const char* const* pathItems,
void* payload)
{
IWebDavCollection& that = *reinterpret_cast<IWebDavCollection*>(payload);
try
{
*isReadOnly = (that.CreateFolder(WebDavConvertPath(pathSize, pathItems)) ? 1 : 0);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_InternalError;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
static OrthancPluginErrorCode WebDavDeleteItemCallback(uint8_t* isReadOnly, /* out */
uint32_t pathSize,
const char* const* pathItems,
void* payload)
{
IWebDavCollection& that = *reinterpret_cast<IWebDavCollection*>(payload);
try
{
*isReadOnly = (that.DeleteItem(WebDavConvertPath(pathSize, pathItems)) ? 1 : 0);
return OrthancPluginErrorCode_Success;
}
catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
{
return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
}
catch (...)
{
return OrthancPluginErrorCode_InternalError;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
void IWebDavCollection::Register(const std::string& uri,
IWebDavCollection& collection)
{
OrthancPluginErrorCode code = OrthancPluginRegisterWebDavCollection(
GetGlobalContext(), uri.c_str(), WebDavIsExistingFolder, WebDavListFolder, WebDavRetrieveFile,
WebDavStoreFileCallback, WebDavCreateFolderCallback, WebDavDeleteItemCallback, &collection);
if (code != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
}
#endif
void GetHttpHeaders(HttpHeaders& result, const OrthancPluginHttpRequest* request)
{
result.clear();
for (uint32_t i = 0; i < request->headersCount; ++i)
{
result[request->headersKeys[i]] = request->headersValues[i];
}
}
void SerializeGetArguments(std::string& output, const OrthancPluginHttpRequest* request)
{
output.clear();
std::vector<std::string> arguments;
for (uint32_t i = 0; i < request->getCount; ++i)
{
if (request->getValues[i] && strlen(request->getValues[i]) > 0)
{
arguments.push_back(std::string(request->getKeys[i]) + "=" + std::string(request->getValues[i]));
}
else
{
arguments.push_back(std::string(request->getKeys[i]));
}
}
output = boost::algorithm::join(arguments, "&");
}
#if !ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4)
static void SetPluginProperty(const std::string& pluginIdentifier,
_OrthancPluginProperty property,
const std::string& value)
{
_OrthancPluginSetPluginProperty params;
params.plugin = pluginIdentifier.c_str();
params.property = property;
params.value = value.c_str();
GetGlobalContext()->InvokeService(GetGlobalContext(), _OrthancPluginService_SetPluginProperty, &params);
}
#endif
void SetRootUri(const std::string& pluginIdentifier,
const std::string& uri)
{
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4)
OrthancPluginSetRootUri2(GetGlobalContext(), pluginIdentifier.c_str(), uri.c_str());
#else
SetPluginProperty(pluginIdentifier, _OrthancPluginProperty_RootUri, uri);
#endif
}
void SetDescription(const std::string& pluginIdentifier,
const std::string& description)
{
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4)
OrthancPluginSetDescription2(GetGlobalContext(), pluginIdentifier.c_str(), description.c_str());
#else
SetPluginProperty(pluginIdentifier, _OrthancPluginProperty_Description, description);
#endif
}
void ExtendOrthancExplorer(const std::string& pluginIdentifier,
const std::string& javascript)
{
#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4)
OrthancPluginExtendOrthancExplorer2(GetGlobalContext(), pluginIdentifier.c_str(), javascript.c_str());
#else
SetPluginProperty(pluginIdentifier, _OrthancPluginProperty_OrthancExplorer, javascript);
#endif
}
#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
RestApiClient::RestApiClient() :
method_(OrthancPluginHttpMethod_Get),
path_("/"),
afterPlugins_(false),
httpStatus_(0)
{
}
RestApiClient::RestApiClient(const char* url,
const OrthancPluginHttpRequest* request) :
method_(request->method),
path_(url),
afterPlugins_(false),
httpStatus_(0)
{
OrthancPlugins::GetHttpHeaders(requestHeaders_, request);
std::string getArguments;
OrthancPlugins::SerializeGetArguments(getArguments, request);
if (!getArguments.empty())
{
path_ += "?" + getArguments;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
void RestApiClient::AddRequestHeader(const std::string& key,
const std::string& value)
{
if (requestHeaders_.find(key) == requestHeaders_.end())
{
requestHeaders_[key] = value;
}
else
{
ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
}
}
#endif
#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
bool RestApiClient::Execute()
{
if (requestBody_.size() > 0xffffffffu)
{
ORTHANC_PLUGINS_LOG_ERROR("Cannot handle body size > 4GB");
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
PluginHttpHeaders converted(requestHeaders_);
MemoryBuffer body;
MemoryBuffer headers;
OrthancPluginErrorCode code = OrthancPluginCallRestApi(GetGlobalContext(), *body, *headers, &httpStatus_, method_, path_.c_str(),
requestHeaders_.size(), converted.GetKeys(), converted.GetValues(),
requestBody_.c_str(), requestBody_.size(), afterPlugins_ ? 1 : 0);
answerHeaders_.clear();
answerBody_.clear();
if (code == OrthancPluginErrorCode_Success)
{
if (httpStatus_ == 0)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
DecodeHttpHeaders(answerHeaders_, headers);
body.ToString(answerBody_);
return true;
}
else
{
if (code == OrthancPluginErrorCode_UnknownResource ||
code == OrthancPluginErrorCode_InexistentItem)
{
httpStatus_ = 404;
return false;
}
else
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
}
}
void RestApiClient::Forward(OrthancPluginContext* context, OrthancPluginRestOutput* output)
{
if (Execute() && httpStatus_ == 200)
{
const char* mimeType = NULL;
for (HttpHeaders::const_iterator h = answerHeaders_.begin(); h != answerHeaders_.end(); ++h)
{
if (h->first == "content-type")
{
mimeType = h->second.c_str();
}
}
AnswerString(answerBody_, mimeType, output);
}
else
{
AnswerHttpError(httpStatus_, output);
}
}
bool RestApiClient::GetAnswerJson(Json::Value& output) const
{
return ReadJson(output, answerBody_);
}
#endif
#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
uint16_t RestApiClient::GetHttpStatus() const
{
if (httpStatus_ == 0)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
}
else
{
return httpStatus_;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
bool RestApiClient::LookupAnswerHeader(std::string& value,
const std::string& key) const
{
if (httpStatus_ == 0)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
}
else
{
HttpHeaders::const_iterator found = answerHeaders_.find(key);
if (found == answerHeaders_.end())
{
return false;
}
else
{
value = found->second;
return true;
}
}
}
#endif
#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
const std::string& RestApiClient::GetAnswerBody() const
{
if (httpStatus_ == 0)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
}
else
{
return answerBody_;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
KeyValueStore::Iterator::Iterator(OrthancPluginKeysValuesIterator *iterator) :
iterator_(iterator)
{
if (iterator_ == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
}
#endif
#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
KeyValueStore::Iterator::~Iterator()
{
OrthancPluginFreeKeysValuesIterator(OrthancPlugins::GetGlobalContext(), iterator_);
}
#endif
#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
bool KeyValueStore::Iterator::Next()
{
uint8_t done;
OrthancPluginErrorCode code = OrthancPluginKeysValuesIteratorNext(OrthancPlugins::GetGlobalContext(), &done, iterator_);
if (code != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
else
{
return (done != 0);
}
}
#endif
#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
std::string KeyValueStore::Iterator::GetKey() const
{
const char* s = OrthancPluginKeysValuesIteratorGetKey(OrthancPlugins::GetGlobalContext(), iterator_);
if (s == NULL)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
}
else
{
return s;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
void KeyValueStore::Iterator::GetValue(std::string& value) const
{
OrthancPlugins::MemoryBuffer valueBuffer;
OrthancPluginErrorCode code = OrthancPluginKeysValuesIteratorGetValue(OrthancPlugins::GetGlobalContext(), *valueBuffer, iterator_);
if (code != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
else
{
valueBuffer.ToString(value);
}
}
#endif
#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
void KeyValueStore::Store(const std::string& key,
const void* value,
size_t valueSize)
{
if (static_cast<size_t>(static_cast<uint32_t>(valueSize)) != valueSize)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory);
}
OrthancPluginErrorCode code = OrthancPluginStoreKeyValue(OrthancPlugins::GetGlobalContext(), storeId_.c_str(),
key.c_str(), value, static_cast<uint32_t>(valueSize));
if (code != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
}
#endif
#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
bool KeyValueStore::GetValue(std::string& value,
const std::string& key)
{
uint8_t found = false;
OrthancPlugins::MemoryBuffer valueBuffer;
OrthancPluginErrorCode code = OrthancPluginGetKeyValue(OrthancPlugins::GetGlobalContext(), &found,
*valueBuffer, storeId_.c_str(), key.c_str());
if (code != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
else if (found)
{
valueBuffer.ToString(value);
return true;
}
else
{
return false;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
void KeyValueStore::DeleteKey(const std::string& key)
{
OrthancPluginErrorCode code = OrthancPluginDeleteKeyValue(OrthancPlugins::GetGlobalContext(),
storeId_.c_str(), key.c_str());
if (code != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
}
#endif
#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
KeyValueStore::Iterator* KeyValueStore::CreateIterator()
{
return new Iterator(OrthancPluginCreateKeysValuesIterator(OrthancPlugins::GetGlobalContext(), storeId_.c_str()));
}
#endif
#if HAS_ORTHANC_PLUGIN_QUEUES == 1
void Queue::Enqueue(const void* value,
size_t valueSize)
{
if (static_cast<size_t>(static_cast<uint32_t>(valueSize)) != valueSize)
{
ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory);
}
OrthancPluginErrorCode code = OrthancPluginEnqueueValue(OrthancPlugins::GetGlobalContext(),
queueId_.c_str(), value, static_cast<uint32_t>(valueSize));
if (code != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
}
#endif
#if HAS_ORTHANC_PLUGIN_QUEUES == 1
bool Queue::DequeueInternal(std::string& value,
OrthancPluginQueueOrigin origin)
{
uint8_t found = false;
OrthancPlugins::MemoryBuffer valueBuffer;
OrthancPluginErrorCode code = OrthancPluginDequeueValue(OrthancPlugins::GetGlobalContext(), &found,
*valueBuffer, queueId_.c_str(), origin);
if (code != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
else if (found)
{
valueBuffer.ToString(value);
return true;
}
else
{
return false;
}
}
#endif
#if HAS_ORTHANC_PLUGIN_QUEUES == 1
uint64_t Queue::GetSize()
{
uint64_t size = 0;
OrthancPluginErrorCode code = OrthancPluginGetQueueSize(OrthancPlugins::GetGlobalContext(), queueId_.c_str(), &size);
if (code != OrthancPluginErrorCode_Success)
{
ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
}
else
{
return size;
}
}
#endif
}