Mercurial > hg > orthanc
view Plugins/Engine/OrthancPlugins.cpp @ 3103:81b58b549845
back to using 'var' instead of 'let' since let is not supported by many old browsers. All variables declaration have been moved to the top of the function to better show that their scope is the function
author | Alain Mazy <alain@mazy.be> |
---|---|
date | Thu, 10 Jan 2019 10:51:36 +0100 |
parents | 4e43e67f8ecf |
children | ce272138f15e 8849677c2cbc |
line wrap: on
line source
/** * Orthanc - A Lightweight, RESTful DICOM Store * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics * Department, University Hospital of Liege, Belgium * Copyright (C) 2017-2019 Osimis S.A., Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * In addition, as a special exception, the copyright holders of this * program give permission to link the code of its release with the * OpenSSL project's "OpenSSL" library (or with modified versions of it * that use the same license as the "OpenSSL" library), and distribute * the linked executables. You must obey the GNU General Public License * in all respects for all of the code used other than "OpenSSL". If you * modify file(s) with this exception, you may extend this exception to * your version of the file(s), but you are not obligated to do so. If * you do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source files * in the program, then also delete it here. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ #include "../../OrthancServer/PrecompiledHeadersServer.h" #include "OrthancPlugins.h" #if ORTHANC_ENABLE_PLUGINS != 1 #error The plugin support is disabled #endif #include "../../Core/ChunkedBuffer.h" #include "../../Core/DicomFormat/DicomArray.h" #include "../../Core/HttpServer/HttpToolbox.h" #include "../../Core/Logging.h" #include "../../Core/OrthancException.h" #include "../../Core/SerializationToolbox.h" #include "../../Core/Toolbox.h" #include "../../Core/DicomParsing/FromDcmtkBridge.h" #include "../../Core/DicomParsing/ToDcmtkBridge.h" #include "../../OrthancServer/OrthancConfiguration.h" #include "../../OrthancServer/ServerContext.h" #include "../../OrthancServer/ServerToolbox.h" #include "../../OrthancServer/Search/HierarchicalMatcher.h" #include "../../Core/DicomParsing/Internals/DicomImageDecoder.h" #include "../../Core/Compression/ZlibCompressor.h" #include "../../Core/Compression/GzipCompressor.h" #include "../../Core/Images/Image.h" #include "../../Core/Images/PngReader.h" #include "../../Core/Images/PngWriter.h" #include "../../Core/Images/JpegReader.h" #include "../../Core/Images/JpegWriter.h" #include "../../Core/Images/ImageProcessing.h" #include "../../OrthancServer/DefaultDicomImageDecoder.h" #include "../../OrthancServer/OrthancFindRequestHandler.h" #include "PluginsEnumerations.h" #include "PluginsJob.h" #include <boost/regex.hpp> #include <dcmtk/dcmdata/dcdict.h> #include <dcmtk/dcmdata/dcdicent.h> namespace Orthanc { static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target, const void* data, size_t size) { target.size = size; if (size == 0) { target.data = NULL; } else { target.data = malloc(size); if (target.data != NULL) { memcpy(target.data, data, size); } else { throw OrthancException(ErrorCode_NotEnoughMemory); } } } static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target, const std::string& str) { if (str.size() == 0) { target.size = 0; target.data = NULL; } else { CopyToMemoryBuffer(target, str.c_str(), str.size()); } } static char* CopyString(const std::string& str) { char *result = reinterpret_cast<char*>(malloc(str.size() + 1)); if (result == NULL) { throw OrthancException(ErrorCode_NotEnoughMemory); } if (str.size() == 0) { result[0] = '\0'; } else { memcpy(result, &str[0], str.size() + 1); } return result; } namespace { class PluginStorageArea : public IStorageArea { private: _OrthancPluginRegisterStorageArea callbacks_; PluginsErrorDictionary& errorDictionary_; void Free(void* buffer) const { if (buffer != NULL) { callbacks_.free(buffer); } } public: PluginStorageArea(const _OrthancPluginRegisterStorageArea& callbacks, PluginsErrorDictionary& errorDictionary) : callbacks_(callbacks), errorDictionary_(errorDictionary) { } virtual void Create(const std::string& uuid, const void* content, size_t size, FileContentType type) { OrthancPluginErrorCode error = callbacks_.create (uuid.c_str(), content, size, Plugins::Convert(type)); if (error != OrthancPluginErrorCode_Success) { errorDictionary_.LogError(error, true); throw OrthancException(static_cast<ErrorCode>(error)); } } virtual void Read(std::string& content, const std::string& uuid, FileContentType type) { void* buffer = NULL; int64_t size = 0; OrthancPluginErrorCode error = callbacks_.read (&buffer, &size, uuid.c_str(), Plugins::Convert(type)); if (error != OrthancPluginErrorCode_Success) { errorDictionary_.LogError(error, true); throw OrthancException(static_cast<ErrorCode>(error)); } try { content.resize(static_cast<size_t>(size)); } catch (...) { Free(buffer); throw OrthancException(ErrorCode_NotEnoughMemory); } if (size > 0) { memcpy(&content[0], buffer, static_cast<size_t>(size)); } Free(buffer); } virtual void Remove(const std::string& uuid, FileContentType type) { OrthancPluginErrorCode error = callbacks_.remove (uuid.c_str(), Plugins::Convert(type)); if (error != OrthancPluginErrorCode_Success) { errorDictionary_.LogError(error, true); throw OrthancException(static_cast<ErrorCode>(error)); } } }; class StorageAreaFactory : public boost::noncopyable { private: SharedLibrary& sharedLibrary_; _OrthancPluginRegisterStorageArea callbacks_; PluginsErrorDictionary& errorDictionary_; public: StorageAreaFactory(SharedLibrary& sharedLibrary, const _OrthancPluginRegisterStorageArea& callbacks, PluginsErrorDictionary& errorDictionary) : sharedLibrary_(sharedLibrary), callbacks_(callbacks), errorDictionary_(errorDictionary) { } SharedLibrary& GetSharedLibrary() { return sharedLibrary_; } IStorageArea* Create() const { return new PluginStorageArea(callbacks_, errorDictionary_); } }; class OrthancPeers : public boost::noncopyable { private: std::vector<std::string> names_; std::vector<WebServiceParameters> parameters_; void CheckIndex(size_t i) const { assert(names_.size() == parameters_.size()); if (i >= names_.size()) { throw OrthancException(ErrorCode_ParameterOutOfRange); } } public: OrthancPeers() { OrthancConfiguration::ReaderLock lock; std::set<std::string> peers; lock.GetConfiguration().GetListOfOrthancPeers(peers); names_.reserve(peers.size()); parameters_.reserve(peers.size()); for (std::set<std::string>::const_iterator it = peers.begin(); it != peers.end(); ++it) { WebServiceParameters peer; if (lock.GetConfiguration().LookupOrthancPeer(peer, *it)) { names_.push_back(*it); parameters_.push_back(peer); } } } size_t GetPeersCount() const { return names_.size(); } const std::string& GetPeerName(size_t i) const { CheckIndex(i); return names_[i]; } const WebServiceParameters& GetPeerParameters(size_t i) const { CheckIndex(i); return parameters_[i]; } }; } class OrthancPlugins::PImpl { private: boost::mutex contextMutex_; ServerContext* context_; public: class PluginHttpOutput : public boost::noncopyable { private: HttpOutput& output_; std::auto_ptr<std::string> errorDetails_; bool logDetails_; public: PluginHttpOutput(HttpOutput& output) : output_(output), logDetails_(false) { } HttpOutput& GetOutput() { return output_; } void SetErrorDetails(const std::string& details, bool logDetails) { errorDetails_.reset(new std::string(details)); logDetails_ = logDetails; } bool HasErrorDetails() const { return errorDetails_.get() != NULL; } bool IsLogDetails() const { return logDetails_; } const std::string& GetErrorDetails() const { if (errorDetails_.get() == NULL) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { return *errorDetails_; } } }; class RestCallback : public boost::noncopyable { private: boost::regex regex_; OrthancPluginRestCallback callback_; bool lock_; OrthancPluginErrorCode InvokeInternal(PluginHttpOutput& output, const std::string& flatUri, const OrthancPluginHttpRequest& request) { return callback_(reinterpret_cast<OrthancPluginRestOutput*>(&output), flatUri.c_str(), &request); } public: RestCallback(const char* regex, OrthancPluginRestCallback callback, bool lockRestCallbacks) : regex_(regex), callback_(callback), lock_(lockRestCallbacks) { } const boost::regex& GetRegularExpression() const { return regex_; } OrthancPluginErrorCode Invoke(boost::recursive_mutex& restCallbackMutex, PluginHttpOutput& output, const std::string& flatUri, const OrthancPluginHttpRequest& request) { if (lock_) { boost::recursive_mutex::scoped_lock lock(restCallbackMutex); return InvokeInternal(output, flatUri, request); } else { return InvokeInternal(output, flatUri, request); } } }; class ServerContextLock { private: boost::mutex::scoped_lock lock_; ServerContext* context_; public: ServerContextLock(PImpl& that) : lock_(that.contextMutex_), context_(that.context_) { if (context_ == NULL) { throw OrthancException(ErrorCode_DatabaseNotInitialized); } } ServerContext& GetContext() { assert(context_ != NULL); return *context_; } }; void SetServerContext(ServerContext* context) { boost::mutex::scoped_lock lock(contextMutex_); context_ = context; } typedef std::pair<std::string, _OrthancPluginProperty> Property; typedef std::list<RestCallback*> RestCallbacks; typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks; typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks; typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters; typedef std::list<OrthancPluginIncomingHttpRequestFilter2> IncomingHttpRequestFilters2; typedef std::list<OrthancPluginDecodeImageCallback> DecodeImageCallbacks; typedef std::list<OrthancPluginJobsUnserializer> JobsUnserializers; typedef std::map<Property, std::string> Properties; PluginsManager manager_; RestCallbacks restCallbacks_; OnStoredCallbacks onStoredCallbacks_; OnChangeCallbacks onChangeCallbacks_; OrthancPluginFindCallback findCallback_; OrthancPluginWorklistCallback worklistCallback_; DecodeImageCallbacks decodeImageCallbacks_; JobsUnserializers jobsUnserializers_; _OrthancPluginMoveCallback moveCallbacks_; IncomingHttpRequestFilters incomingHttpRequestFilters_; IncomingHttpRequestFilters2 incomingHttpRequestFilters2_; std::auto_ptr<StorageAreaFactory> storageArea_; boost::recursive_mutex restCallbackMutex_; boost::recursive_mutex storedCallbackMutex_; boost::recursive_mutex changeCallbackMutex_; boost::mutex findCallbackMutex_; boost::mutex worklistCallbackMutex_; boost::mutex decodeImageCallbackMutex_; boost::mutex jobsUnserializersMutex_; boost::recursive_mutex invokeServiceMutex_; Properties properties_; int argc_; char** argv_; std::auto_ptr<OrthancPluginDatabase> database_; PluginsErrorDictionary dictionary_; PImpl() : context_(NULL), findCallback_(NULL), worklistCallback_(NULL), argc_(1), argv_(NULL) { memset(&moveCallbacks_, 0, sizeof(moveCallbacks_)); } }; class OrthancPlugins::WorklistHandler : public IWorklistRequestHandler { private: OrthancPlugins& that_; std::auto_ptr<HierarchicalMatcher> matcher_; std::auto_ptr<ParsedDicomFile> filtered_; ParsedDicomFile* currentQuery_; void Reset() { matcher_.reset(); filtered_.reset(); currentQuery_ = NULL; } public: WorklistHandler(OrthancPlugins& that) : that_(that) { Reset(); } virtual void Handle(DicomFindAnswers& answers, ParsedDicomFile& query, const std::string& remoteIp, const std::string& remoteAet, const std::string& calledAet, ModalityManufacturer manufacturer) { static const char* LUA_CALLBACK = "IncomingWorklistRequestFilter"; { PImpl::ServerContextLock lock(*that_.pimpl_); LuaScripting::Lock lua(lock.GetContext().GetLuaScripting()); if (!lua.GetLua().IsExistingFunction(LUA_CALLBACK)) { currentQuery_ = &query; } else { Json::Value source, origin; query.DatasetToJson(source, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); OrthancFindRequestHandler::FormatOrigin (origin, remoteIp, remoteAet, calledAet, manufacturer); LuaFunctionCall call(lua.GetLua(), LUA_CALLBACK); call.PushJson(source); call.PushJson(origin); Json::Value target; call.ExecuteToJson(target, true); filtered_.reset(ParsedDicomFile::CreateFromJson(target, DicomFromJsonFlags_None)); currentQuery_ = filtered_.get(); } } matcher_.reset(new HierarchicalMatcher(*currentQuery_)); { boost::mutex::scoped_lock lock(that_.pimpl_->worklistCallbackMutex_); if (that_.pimpl_->worklistCallback_) { OrthancPluginErrorCode error = that_.pimpl_->worklistCallback_ (reinterpret_cast<OrthancPluginWorklistAnswers*>(&answers), reinterpret_cast<const OrthancPluginWorklistQuery*>(this), remoteAet.c_str(), calledAet.c_str()); if (error != OrthancPluginErrorCode_Success) { Reset(); that_.GetErrorDictionary().LogError(error, true); throw OrthancException(static_cast<ErrorCode>(error)); } } Reset(); } } void GetDicomQuery(OrthancPluginMemoryBuffer& target) const { if (currentQuery_ == NULL) { throw OrthancException(ErrorCode_Plugin); } std::string dicom; currentQuery_->SaveToMemoryBuffer(dicom); CopyToMemoryBuffer(target, dicom.c_str(), dicom.size()); } bool IsMatch(const void* dicom, size_t size) const { if (matcher_.get() == NULL) { throw OrthancException(ErrorCode_Plugin); } ParsedDicomFile f(dicom, size); return matcher_->Match(f); } void AddAnswer(OrthancPluginWorklistAnswers* answers, const void* dicom, size_t size) const { if (matcher_.get() == NULL) { throw OrthancException(ErrorCode_Plugin); } ParsedDicomFile f(dicom, size); std::auto_ptr<ParsedDicomFile> summary(matcher_->Extract(f)); reinterpret_cast<DicomFindAnswers*>(answers)->Add(*summary); } }; class OrthancPlugins::FindHandler : public IFindRequestHandler { private: OrthancPlugins& that_; std::auto_ptr<DicomArray> currentQuery_; void Reset() { currentQuery_.reset(NULL); } public: FindHandler(OrthancPlugins& that) : that_(that) { Reset(); } virtual void Handle(DicomFindAnswers& answers, const DicomMap& input, const std::list<DicomTag>& sequencesToReturn, const std::string& remoteIp, const std::string& remoteAet, const std::string& calledAet, ModalityManufacturer manufacturer) { DicomMap tmp; tmp.Assign(input); for (std::list<DicomTag>::const_iterator it = sequencesToReturn.begin(); it != sequencesToReturn.end(); ++it) { if (!input.HasTag(*it)) { tmp.SetValue(*it, "", false); } } { boost::mutex::scoped_lock lock(that_.pimpl_->findCallbackMutex_); currentQuery_.reset(new DicomArray(tmp)); if (that_.pimpl_->findCallback_) { OrthancPluginErrorCode error = that_.pimpl_->findCallback_ (reinterpret_cast<OrthancPluginFindAnswers*>(&answers), reinterpret_cast<const OrthancPluginFindQuery*>(this), remoteAet.c_str(), calledAet.c_str()); if (error != OrthancPluginErrorCode_Success) { Reset(); that_.GetErrorDictionary().LogError(error, true); throw OrthancException(static_cast<ErrorCode>(error)); } } Reset(); } } void Invoke(_OrthancPluginService service, const _OrthancPluginFindOperation& operation) const { if (currentQuery_.get() == NULL) { throw OrthancException(ErrorCode_Plugin); } switch (service) { case _OrthancPluginService_GetFindQuerySize: *operation.resultUint32 = currentQuery_->GetSize(); break; case _OrthancPluginService_GetFindQueryTag: { const DicomTag& tag = currentQuery_->GetElement(operation.index).GetTag(); *operation.resultGroup = tag.GetGroup(); *operation.resultElement = tag.GetElement(); break; } case _OrthancPluginService_GetFindQueryTagName: { const DicomElement& element = currentQuery_->GetElement(operation.index); *operation.resultString = CopyString(FromDcmtkBridge::GetTagName(element)); break; } case _OrthancPluginService_GetFindQueryValue: { *operation.resultString = CopyString(currentQuery_->GetElement(operation.index).GetValue().GetContent()); break; } default: throw OrthancException(ErrorCode_InternalError); } } }; class OrthancPlugins::MoveHandler : public IMoveRequestHandler { private: class Driver : public IMoveRequestIterator { private: void* driver_; unsigned int count_; unsigned int pos_; OrthancPluginApplyMove apply_; OrthancPluginFreeMove free_; public: Driver(void* driver, unsigned int count, OrthancPluginApplyMove apply, OrthancPluginFreeMove free) : driver_(driver), count_(count), pos_(0), apply_(apply), free_(free) { if (driver_ == NULL) { throw OrthancException(ErrorCode_Plugin); } } virtual ~Driver() { if (driver_ != NULL) { free_(driver_); driver_ = NULL; } } virtual unsigned int GetSubOperationCount() const { return count_; } virtual Status DoNext() { if (pos_ >= count_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { OrthancPluginErrorCode error = apply_(driver_); if (error != OrthancPluginErrorCode_Success) { LOG(ERROR) << "Error while doing C-Move from plugin: " << EnumerationToString(static_cast<ErrorCode>(error)); return Status_Failure; } else { pos_++; return Status_Success; } } } }; _OrthancPluginMoveCallback params_; static std::string ReadTag(const DicomMap& input, const DicomTag& tag) { const DicomValue* value = input.TestAndGetValue(tag); if (value != NULL && !value->IsBinary() && !value->IsNull()) { return value->GetContent(); } else { return std::string(); } } public: MoveHandler(OrthancPlugins& that) { boost::recursive_mutex::scoped_lock lock(that.pimpl_->invokeServiceMutex_); params_ = that.pimpl_->moveCallbacks_; if (params_.callback == NULL || params_.getMoveSize == NULL || params_.applyMove == NULL || params_.freeMove == NULL) { throw OrthancException(ErrorCode_Plugin); } } virtual IMoveRequestIterator* Handle(const std::string& targetAet, const DicomMap& input, const std::string& originatorIp, const std::string& originatorAet, const std::string& calledAet, uint16_t originatorId) { std::string levelString = ReadTag(input, DICOM_TAG_QUERY_RETRIEVE_LEVEL); std::string patientId = ReadTag(input, DICOM_TAG_PATIENT_ID); std::string accessionNumber = ReadTag(input, DICOM_TAG_ACCESSION_NUMBER); std::string studyInstanceUid = ReadTag(input, DICOM_TAG_STUDY_INSTANCE_UID); std::string seriesInstanceUid = ReadTag(input, DICOM_TAG_SERIES_INSTANCE_UID); std::string sopInstanceUid = ReadTag(input, DICOM_TAG_SOP_INSTANCE_UID); OrthancPluginResourceType level = OrthancPluginResourceType_None; if (!levelString.empty()) { level = Plugins::Convert(StringToResourceType(levelString.c_str())); } void* driver = params_.callback(level, patientId.empty() ? NULL : patientId.c_str(), accessionNumber.empty() ? NULL : accessionNumber.c_str(), studyInstanceUid.empty() ? NULL : studyInstanceUid.c_str(), seriesInstanceUid.empty() ? NULL : seriesInstanceUid.c_str(), sopInstanceUid.empty() ? NULL : sopInstanceUid.c_str(), originatorAet.c_str(), calledAet.c_str(), targetAet.c_str(), originatorId); if (driver == NULL) { throw OrthancException(ErrorCode_Plugin, "Plugin cannot create a driver for an incoming C-MOVE request"); } unsigned int size = params_.getMoveSize(driver); return new Driver(driver, size, params_.applyMove, params_.freeMove); } }; OrthancPlugins::OrthancPlugins() { /* Sanity check of the compiler */ if (sizeof(int32_t) != sizeof(OrthancPluginErrorCode) || sizeof(int32_t) != sizeof(OrthancPluginHttpMethod) || sizeof(int32_t) != sizeof(_OrthancPluginService) || sizeof(int32_t) != sizeof(_OrthancPluginProperty) || sizeof(int32_t) != sizeof(OrthancPluginPixelFormat) || sizeof(int32_t) != sizeof(OrthancPluginContentType) || sizeof(int32_t) != sizeof(OrthancPluginResourceType) || sizeof(int32_t) != sizeof(OrthancPluginChangeType) || sizeof(int32_t) != sizeof(OrthancPluginImageFormat) || sizeof(int32_t) != sizeof(OrthancPluginCompressionType) || sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation) || sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFlags) || sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFormat) || sizeof(int32_t) != sizeof(OrthancPluginCreateDicomFlags) || sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType) || sizeof(int32_t) != sizeof(OrthancPluginIdentifierConstraint) || sizeof(int32_t) != sizeof(OrthancPluginInstanceOrigin) || sizeof(int32_t) != sizeof(OrthancPluginJobStepStatus) || static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeBinary) != static_cast<int>(DicomToJsonFlags_IncludeBinary) || static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePrivateTags) != static_cast<int>(DicomToJsonFlags_IncludePrivateTags) || static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeUnknownTags) != static_cast<int>(DicomToJsonFlags_IncludeUnknownTags) || static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePixelData) != static_cast<int>(DicomToJsonFlags_IncludePixelData) || static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToNull) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToNull) || static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToAscii) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToAscii) || static_cast<int>(OrthancPluginCreateDicomFlags_DecodeDataUriScheme) != static_cast<int>(DicomFromJsonFlags_DecodeDataUriScheme) || static_cast<int>(OrthancPluginCreateDicomFlags_GenerateIdentifiers) != static_cast<int>(DicomFromJsonFlags_GenerateIdentifiers)) { throw OrthancException(ErrorCode_Plugin); } pimpl_.reset(new PImpl()); pimpl_->manager_.RegisterServiceProvider(*this); } void OrthancPlugins::SetServerContext(ServerContext& context) { pimpl_->SetServerContext(&context); } void OrthancPlugins::ResetServerContext() { pimpl_->SetServerContext(NULL); } OrthancPlugins::~OrthancPlugins() { for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin(); it != pimpl_->restCallbacks_.end(); ++it) { delete *it; } } static void ArgumentsToPlugin(std::vector<const char*>& keys, std::vector<const char*>& values, const IHttpHandler::Arguments& arguments) { keys.resize(arguments.size()); values.resize(arguments.size()); size_t pos = 0; for (IHttpHandler::Arguments::const_iterator it = arguments.begin(); it != arguments.end(); ++it) { keys[pos] = it->first.c_str(); values[pos] = it->second.c_str(); pos++; } } static void ArgumentsToPlugin(std::vector<const char*>& keys, std::vector<const char*>& values, const IHttpHandler::GetArguments& arguments) { keys.resize(arguments.size()); values.resize(arguments.size()); for (size_t i = 0; i < arguments.size(); i++) { keys[i] = arguments[i].first.c_str(); values[i] = arguments[i].second.c_str(); } } bool OrthancPlugins::Handle(HttpOutput& output, RequestOrigin /*origin*/, const char* /*remoteIp*/, const char* /*username*/, HttpMethod method, const UriComponents& uri, const Arguments& headers, const GetArguments& getArguments, const char* bodyData, size_t bodySize) { std::string flatUri = Toolbox::FlattenUri(uri); PImpl::RestCallback* callback = NULL; std::vector<std::string> groups; std::vector<const char*> cgroups; // Loop over the callbacks registered by the plugins bool found = false; for (PImpl::RestCallbacks::const_iterator it = pimpl_->restCallbacks_.begin(); it != pimpl_->restCallbacks_.end() && !found; ++it) { // Check whether the regular expression associated to this // callback matches the URI boost::cmatch what; if (boost::regex_match(flatUri.c_str(), what, (*it)->GetRegularExpression())) { callback = *it; // Extract the value of the free parameters of the regular expression if (what.size() > 1) { groups.resize(what.size() - 1); cgroups.resize(what.size() - 1); for (size_t i = 1; i < what.size(); i++) { groups[i - 1] = what[i]; cgroups[i - 1] = groups[i - 1].c_str(); } } } } if (callback == NULL) { // Callback not found return false; } LOG(INFO) << "Delegating HTTP request to plugin for URI: " << flatUri; std::vector<const char*> getKeys, getValues, headersKeys, headersValues; OrthancPluginHttpRequest request; memset(&request, 0, sizeof(OrthancPluginHttpRequest)); ArgumentsToPlugin(headersKeys, headersValues, headers); switch (method) { case HttpMethod_Get: request.method = OrthancPluginHttpMethod_Get; ArgumentsToPlugin(getKeys, getValues, getArguments); break; case HttpMethod_Post: request.method = OrthancPluginHttpMethod_Post; break; case HttpMethod_Delete: request.method = OrthancPluginHttpMethod_Delete; break; case HttpMethod_Put: request.method = OrthancPluginHttpMethod_Put; break; default: throw OrthancException(ErrorCode_InternalError); } request.groups = (cgroups.size() ? &cgroups[0] : NULL); request.groupsCount = cgroups.size(); request.getCount = getArguments.size(); request.body = bodyData; request.bodySize = bodySize; request.headersCount = headers.size(); if (getArguments.size() > 0) { request.getKeys = &getKeys[0]; request.getValues = &getValues[0]; } if (headers.size() > 0) { request.headersKeys = &headersKeys[0]; request.headersValues = &headersValues[0]; } assert(callback != NULL); PImpl::PluginHttpOutput pluginOutput(output); OrthancPluginErrorCode error = callback->Invoke (pimpl_->restCallbackMutex_, pluginOutput, flatUri, request); if (error == OrthancPluginErrorCode_Success && output.IsWritingMultipart()) { output.CloseMultipart(); } if (error == OrthancPluginErrorCode_Success) { return true; } else { GetErrorDictionary().LogError(error, false); if (pluginOutput.HasErrorDetails()) { throw OrthancException(static_cast<ErrorCode>(error), pluginOutput.GetErrorDetails(), pluginOutput.IsLogDetails()); } else { throw OrthancException(static_cast<ErrorCode>(error)); } } } void OrthancPlugins::SignalStoredInstance(const std::string& instanceId, DicomInstanceToStore& instance, const Json::Value& simplifiedTags) { boost::recursive_mutex::scoped_lock lock(pimpl_->storedCallbackMutex_); for (PImpl::OnStoredCallbacks::const_iterator callback = pimpl_->onStoredCallbacks_.begin(); callback != pimpl_->onStoredCallbacks_.end(); ++callback) { OrthancPluginErrorCode error = (*callback) (reinterpret_cast<OrthancPluginDicomInstance*>(&instance), instanceId.c_str()); if (error != OrthancPluginErrorCode_Success) { GetErrorDictionary().LogError(error, true); throw OrthancException(static_cast<ErrorCode>(error)); } } } void OrthancPlugins::SignalChangeInternal(OrthancPluginChangeType changeType, OrthancPluginResourceType resourceType, const char* resource) { boost::recursive_mutex::scoped_lock lock(pimpl_->changeCallbackMutex_); for (std::list<OrthancPluginOnChangeCallback>::const_iterator callback = pimpl_->onChangeCallbacks_.begin(); callback != pimpl_->onChangeCallbacks_.end(); ++callback) { OrthancPluginErrorCode error = (*callback) (changeType, resourceType, resource); if (error != OrthancPluginErrorCode_Success) { GetErrorDictionary().LogError(error, true); throw OrthancException(static_cast<ErrorCode>(error)); } } } void OrthancPlugins::SignalChange(const ServerIndexChange& change) { SignalChangeInternal(Plugins::Convert(change.GetChangeType()), Plugins::Convert(change.GetResourceType()), change.GetPublicId().c_str()); } void OrthancPlugins::RegisterRestCallback(const void* parameters, bool lock) { const _OrthancPluginRestCallback& p = *reinterpret_cast<const _OrthancPluginRestCallback*>(parameters); LOG(INFO) << "Plugin has registered a REST callback " << (lock ? "with" : "without") << " mutual exclusion on: " << p.pathRegularExpression; pimpl_->restCallbacks_.push_back(new PImpl::RestCallback(p.pathRegularExpression, p.callback, lock)); } void OrthancPlugins::RegisterOnStoredInstanceCallback(const void* parameters) { const _OrthancPluginOnStoredInstanceCallback& p = *reinterpret_cast<const _OrthancPluginOnStoredInstanceCallback*>(parameters); LOG(INFO) << "Plugin has registered an OnStoredInstance callback"; pimpl_->onStoredCallbacks_.push_back(p.callback); } void OrthancPlugins::RegisterOnChangeCallback(const void* parameters) { const _OrthancPluginOnChangeCallback& p = *reinterpret_cast<const _OrthancPluginOnChangeCallback*>(parameters); LOG(INFO) << "Plugin has registered an OnChange callback"; pimpl_->onChangeCallbacks_.push_back(p.callback); } void OrthancPlugins::RegisterWorklistCallback(const void* parameters) { const _OrthancPluginWorklistCallback& p = *reinterpret_cast<const _OrthancPluginWorklistCallback*>(parameters); boost::mutex::scoped_lock lock(pimpl_->worklistCallbackMutex_); if (pimpl_->worklistCallback_ != NULL) { throw OrthancException(ErrorCode_Plugin, "Can only register one plugin to handle modality worklists"); } else { LOG(INFO) << "Plugin has registered a callback to handle modality worklists"; pimpl_->worklistCallback_ = p.callback; } } void OrthancPlugins::RegisterFindCallback(const void* parameters) { const _OrthancPluginFindCallback& p = *reinterpret_cast<const _OrthancPluginFindCallback*>(parameters); boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_); if (pimpl_->findCallback_ != NULL) { throw OrthancException(ErrorCode_Plugin, "Can only register one plugin to handle C-FIND requests"); } else { LOG(INFO) << "Plugin has registered a callback to handle C-FIND requests"; pimpl_->findCallback_ = p.callback; } } void OrthancPlugins::RegisterMoveCallback(const void* parameters) { // invokeServiceMutex_ is assumed to be locked const _OrthancPluginMoveCallback& p = *reinterpret_cast<const _OrthancPluginMoveCallback*>(parameters); if (pimpl_->moveCallbacks_.callback != NULL) { throw OrthancException(ErrorCode_Plugin, "Can only register one plugin to handle C-MOVE requests"); } else { LOG(INFO) << "Plugin has registered a callback to handle C-MOVE requests"; pimpl_->moveCallbacks_ = p; } } void OrthancPlugins::RegisterDecodeImageCallback(const void* parameters) { const _OrthancPluginDecodeImageCallback& p = *reinterpret_cast<const _OrthancPluginDecodeImageCallback*>(parameters); boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_); pimpl_->decodeImageCallbacks_.push_back(p.callback); LOG(INFO) << "Plugin has registered a callback to decode DICOM images (" << pimpl_->decodeImageCallbacks_.size() << " decoder(s) now active)"; } void OrthancPlugins::RegisterJobsUnserializer(const void* parameters) { const _OrthancPluginJobsUnserializer& p = *reinterpret_cast<const _OrthancPluginJobsUnserializer*>(parameters); boost::mutex::scoped_lock lock(pimpl_->jobsUnserializersMutex_); pimpl_->jobsUnserializers_.push_back(p.unserializer); LOG(INFO) << "Plugin has registered a callback to unserialize jobs (" << pimpl_->jobsUnserializers_.size() << " unserializer(s) now active)"; } void OrthancPlugins::RegisterIncomingHttpRequestFilter(const void* parameters) { const _OrthancPluginIncomingHttpRequestFilter& p = *reinterpret_cast<const _OrthancPluginIncomingHttpRequestFilter*>(parameters); LOG(INFO) << "Plugin has registered a callback to filter incoming HTTP requests"; pimpl_->incomingHttpRequestFilters_.push_back(p.callback); } void OrthancPlugins::RegisterIncomingHttpRequestFilter2(const void* parameters) { const _OrthancPluginIncomingHttpRequestFilter2& p = *reinterpret_cast<const _OrthancPluginIncomingHttpRequestFilter2*>(parameters); LOG(INFO) << "Plugin has registered a callback to filter incoming HTTP requests"; pimpl_->incomingHttpRequestFilters2_.push_back(p.callback); } void OrthancPlugins::AnswerBuffer(const void* parameters) { const _OrthancPluginAnswerBuffer& p = *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters); HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); translatedOutput.SetContentType(p.mimeType); translatedOutput.Answer(p.answer, p.answerSize); } void OrthancPlugins::Redirect(const void* parameters) { const _OrthancPluginOutputPlusArgument& p = *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters); HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); translatedOutput.Redirect(p.argument); } void OrthancPlugins::SendHttpStatusCode(const void* parameters) { const _OrthancPluginSendHttpStatusCode& p = *reinterpret_cast<const _OrthancPluginSendHttpStatusCode*>(parameters); HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); translatedOutput.SendStatus(static_cast<HttpStatus>(p.status)); } void OrthancPlugins::SendHttpStatus(const void* parameters) { const _OrthancPluginSendHttpStatus& p = *reinterpret_cast<const _OrthancPluginSendHttpStatus*>(parameters); HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); HttpStatus status = static_cast<HttpStatus>(p.status); if (p.bodySize > 0 && p.body != NULL) { translatedOutput.SendStatus(status, p.body, p.bodySize); } else { translatedOutput.SendStatus(status); } } void OrthancPlugins::SendUnauthorized(const void* parameters) { const _OrthancPluginOutputPlusArgument& p = *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters); HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); translatedOutput.SendUnauthorized(p.argument); } void OrthancPlugins::SendMethodNotAllowed(const void* parameters) { const _OrthancPluginOutputPlusArgument& p = *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters); HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); translatedOutput.SendMethodNotAllowed(p.argument); } void OrthancPlugins::SetCookie(const void* parameters) { const _OrthancPluginSetHttpHeader& p = *reinterpret_cast<const _OrthancPluginSetHttpHeader*>(parameters); HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); translatedOutput.SetCookie(p.key, p.value); } void OrthancPlugins::SetHttpHeader(const void* parameters) { const _OrthancPluginSetHttpHeader& p = *reinterpret_cast<const _OrthancPluginSetHttpHeader*>(parameters); HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); translatedOutput.AddHeader(p.key, p.value); } void OrthancPlugins::SetHttpErrorDetails(const void* parameters) { const _OrthancPluginSetHttpErrorDetails& p = *reinterpret_cast<const _OrthancPluginSetHttpErrorDetails*>(parameters); PImpl::PluginHttpOutput* output = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output); output->SetErrorDetails(p.details, p.log); } void OrthancPlugins::CompressAndAnswerPngImage(const void* parameters) { // Bridge for backward compatibility with Orthanc <= 0.9.3 const _OrthancPluginCompressAndAnswerPngImage& p = *reinterpret_cast<const _OrthancPluginCompressAndAnswerPngImage*>(parameters); _OrthancPluginCompressAndAnswerImage p2; p2.output = p.output; p2.imageFormat = OrthancPluginImageFormat_Png; p2.pixelFormat = p.format; p2.width = p.width; p2.height = p.height; p2.pitch = p.height; p2.buffer = p.buffer; p2.quality = 0; CompressAndAnswerImage(&p2); } void OrthancPlugins::CompressAndAnswerImage(const void* parameters) { const _OrthancPluginCompressAndAnswerImage& p = *reinterpret_cast<const _OrthancPluginCompressAndAnswerImage*>(parameters); HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); ImageAccessor accessor; accessor.AssignReadOnly(Plugins::Convert(p.pixelFormat), p.width, p.height, p.pitch, p.buffer); std::string compressed; switch (p.imageFormat) { case OrthancPluginImageFormat_Png: { PngWriter writer; writer.WriteToMemory(compressed, accessor); translatedOutput.SetContentType(MimeType_Png); break; } case OrthancPluginImageFormat_Jpeg: { JpegWriter writer; writer.SetQuality(p.quality); writer.WriteToMemory(compressed, accessor); translatedOutput.SetContentType(MimeType_Jpeg); break; } default: throw OrthancException(ErrorCode_ParameterOutOfRange); } translatedOutput.Answer(compressed); } void OrthancPlugins::GetDicomForInstance(const void* parameters) { const _OrthancPluginGetDicomForInstance& p = *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters); std::string dicom; { PImpl::ServerContextLock lock(*pimpl_); lock.GetContext().ReadDicom(dicom, p.instanceId); } CopyToMemoryBuffer(*p.target, dicom); } void OrthancPlugins::RestApiGet(const void* parameters, bool afterPlugins) { const _OrthancPluginRestApiGet& p = *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters); LOG(INFO) << "Plugin making REST GET call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)"); IHttpHandler* handler; { PImpl::ServerContextLock lock(*pimpl_); handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); } std::string result; if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri)) { CopyToMemoryBuffer(*p.target, result); } else { throw OrthancException(ErrorCode_UnknownResource); } } void OrthancPlugins::RestApiGet2(const void* parameters) { const _OrthancPluginRestApiGet2& p = *reinterpret_cast<const _OrthancPluginRestApiGet2*>(parameters); LOG(INFO) << "Plugin making REST GET call on URI " << p.uri << (p.afterPlugins ? " (after plugins)" : " (built-in API)"); IHttpHandler::Arguments headers; for (uint32_t i = 0; i < p.headersCount; i++) { std::string name(p.headersKeys[i]); std::transform(name.begin(), name.end(), name.begin(), ::tolower); headers[name] = p.headersValues[i]; } IHttpHandler* handler; { PImpl::ServerContextLock lock(*pimpl_); handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins); } std::string result; if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri, headers)) { CopyToMemoryBuffer(*p.target, result); } else { throw OrthancException(ErrorCode_UnknownResource); } } void OrthancPlugins::RestApiPostPut(bool isPost, const void* parameters, bool afterPlugins) { const _OrthancPluginRestApiPostPut& p = *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters); LOG(INFO) << "Plugin making REST " << EnumerationToString(isPost ? HttpMethod_Post : HttpMethod_Put) << " call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)"); IHttpHandler* handler; { PImpl::ServerContextLock lock(*pimpl_); handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); } std::string result; if (isPost ? HttpToolbox::SimplePost(result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize) : HttpToolbox::SimplePut (result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize)) { CopyToMemoryBuffer(*p.target, result); } else { throw OrthancException(ErrorCode_UnknownResource); } } void OrthancPlugins::RestApiDelete(const void* parameters, bool afterPlugins) { const char* uri = reinterpret_cast<const char*>(parameters); LOG(INFO) << "Plugin making REST DELETE call on URI " << uri << (afterPlugins ? " (after plugins)" : " (built-in API)"); IHttpHandler* handler; { PImpl::ServerContextLock lock(*pimpl_); handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); } if (!HttpToolbox::SimpleDelete(*handler, RequestOrigin_Plugins, uri)) { throw OrthancException(ErrorCode_UnknownResource); } } void OrthancPlugins::LookupResource(_OrthancPluginService service, const void* parameters) { const _OrthancPluginRetrieveDynamicString& p = *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters); /** * The enumeration below only uses the tags that are indexed in * the Orthanc database. It reflects the * "CandidateResources::ApplyFilter()" method of the * "OrthancFindRequestHandler" class. **/ DicomTag tag(0, 0); ResourceType level; switch (service) { case _OrthancPluginService_LookupPatient: tag = DICOM_TAG_PATIENT_ID; level = ResourceType_Patient; break; case _OrthancPluginService_LookupStudy: tag = DICOM_TAG_STUDY_INSTANCE_UID; level = ResourceType_Study; break; case _OrthancPluginService_LookupStudyWithAccessionNumber: tag = DICOM_TAG_ACCESSION_NUMBER; level = ResourceType_Study; break; case _OrthancPluginService_LookupSeries: tag = DICOM_TAG_SERIES_INSTANCE_UID; level = ResourceType_Series; break; case _OrthancPluginService_LookupInstance: tag = DICOM_TAG_SOP_INSTANCE_UID; level = ResourceType_Instance; break; default: throw OrthancException(ErrorCode_InternalError); } std::list<std::string> result; { PImpl::ServerContextLock lock(*pimpl_); lock.GetContext().GetIndex().LookupIdentifierExact(result, level, tag, p.argument); } if (result.size() == 1) { *p.result = CopyString(result.front()); } else { throw OrthancException(ErrorCode_UnknownResource); } } static void AccessInstanceMetadataInternal(bool checkExistence, const _OrthancPluginAccessDicomInstance& params, const DicomInstanceToStore& instance) { MetadataType metadata; try { metadata = StringToMetadata(params.key); } catch (OrthancException&) { // Unknown metadata if (checkExistence) { *params.resultInt64 = -1; } else { *params.resultString = NULL; } return; } ServerIndex::MetadataMap::const_iterator it = instance.GetMetadata().find(std::make_pair(ResourceType_Instance, metadata)); if (checkExistence) { if (it != instance.GetMetadata().end()) { *params.resultInt64 = 1; } else { *params.resultInt64 = 0; } } else { if (it != instance.GetMetadata().end()) { *params.resultString = it->second.c_str(); } else { // Error: Missing metadata *params.resultString = NULL; } } } static void AccessDicomInstance(_OrthancPluginService service, const void* parameters) { const _OrthancPluginAccessDicomInstance& p = *reinterpret_cast<const _OrthancPluginAccessDicomInstance*>(parameters); DicomInstanceToStore& instance = *reinterpret_cast<DicomInstanceToStore*>(p.instance); switch (service) { case _OrthancPluginService_GetInstanceRemoteAet: *p.resultString = instance.GetOrigin().GetRemoteAetC(); return; case _OrthancPluginService_GetInstanceSize: *p.resultInt64 = instance.GetBufferSize(); return; case _OrthancPluginService_GetInstanceData: *p.resultString = instance.GetBufferData(); return; case _OrthancPluginService_HasInstanceMetadata: AccessInstanceMetadataInternal(true, p, instance); return; case _OrthancPluginService_GetInstanceMetadata: AccessInstanceMetadataInternal(false, p, instance); return; case _OrthancPluginService_GetInstanceJson: case _OrthancPluginService_GetInstanceSimplifiedJson: { Json::StyledWriter writer; std::string s; if (service == _OrthancPluginService_GetInstanceJson) { s = writer.write(instance.GetJson()); } else { Json::Value simplified; ServerToolbox::SimplifyTags(simplified, instance.GetJson(), DicomToJsonFormat_Human); s = writer.write(simplified); } *p.resultStringToFree = CopyString(s); return; } case _OrthancPluginService_GetInstanceOrigin: // New in Orthanc 0.9.5 *p.resultOrigin = Plugins::Convert(instance.GetOrigin().GetRequestOrigin()); return; default: throw OrthancException(ErrorCode_InternalError); } } void OrthancPlugins::BufferCompression(const void* parameters) { const _OrthancPluginBufferCompression& p = *reinterpret_cast<const _OrthancPluginBufferCompression*>(parameters); std::string result; { std::auto_ptr<DeflateBaseCompressor> compressor; switch (p.compression) { case OrthancPluginCompressionType_Zlib: { compressor.reset(new ZlibCompressor); compressor->SetPrefixWithUncompressedSize(false); break; } case OrthancPluginCompressionType_ZlibWithSize: { compressor.reset(new ZlibCompressor); compressor->SetPrefixWithUncompressedSize(true); break; } case OrthancPluginCompressionType_Gzip: { compressor.reset(new GzipCompressor); compressor->SetPrefixWithUncompressedSize(false); break; } case OrthancPluginCompressionType_GzipWithSize: { compressor.reset(new GzipCompressor); compressor->SetPrefixWithUncompressedSize(true); break; } default: throw OrthancException(ErrorCode_ParameterOutOfRange); } if (p.uncompress) { compressor->Uncompress(result, p.source, p.size); } else { compressor->Compress(result, p.source, p.size); } } CopyToMemoryBuffer(*p.target, result); } static OrthancPluginImage* ReturnImage(std::auto_ptr<ImageAccessor>& image) { // Images returned to plugins are assumed to be writeable. If the // input image is read-only, we return a copy so that it can be modified. if (image->IsReadOnly()) { std::auto_ptr<Image> copy(new Image(image->GetFormat(), image->GetWidth(), image->GetHeight(), false)); ImageProcessing::Copy(*copy, *image); image.reset(NULL); return reinterpret_cast<OrthancPluginImage*>(copy.release()); } else { return reinterpret_cast<OrthancPluginImage*>(image.release()); } } void OrthancPlugins::UncompressImage(const void* parameters) { const _OrthancPluginUncompressImage& p = *reinterpret_cast<const _OrthancPluginUncompressImage*>(parameters); std::auto_ptr<ImageAccessor> image; switch (p.format) { case OrthancPluginImageFormat_Png: { image.reset(new PngReader); reinterpret_cast<PngReader&>(*image).ReadFromMemory(p.data, p.size); break; } case OrthancPluginImageFormat_Jpeg: { image.reset(new JpegReader); reinterpret_cast<JpegReader&>(*image).ReadFromMemory(p.data, p.size); break; } case OrthancPluginImageFormat_Dicom: { image.reset(Decode(p.data, p.size, 0)); break; } default: throw OrthancException(ErrorCode_ParameterOutOfRange); } *(p.target) = ReturnImage(image); } void OrthancPlugins::CompressImage(const void* parameters) { const _OrthancPluginCompressImage& p = *reinterpret_cast<const _OrthancPluginCompressImage*>(parameters); std::string compressed; ImageAccessor accessor; accessor.AssignReadOnly(Plugins::Convert(p.pixelFormat), p.width, p.height, p.pitch, p.buffer); switch (p.imageFormat) { case OrthancPluginImageFormat_Png: { PngWriter writer; writer.WriteToMemory(compressed, accessor); break; } case OrthancPluginImageFormat_Jpeg: { JpegWriter writer; writer.SetQuality(p.quality); writer.WriteToMemory(compressed, accessor); break; } default: throw OrthancException(ErrorCode_ParameterOutOfRange); } CopyToMemoryBuffer(*p.target, compressed.size() > 0 ? compressed.c_str() : NULL, compressed.size()); } void OrthancPlugins::CallHttpClient(const void* parameters) { const _OrthancPluginCallHttpClient& p = *reinterpret_cast<const _OrthancPluginCallHttpClient*>(parameters); HttpClient client; client.SetUrl(p.url); if (p.username != NULL && p.password != NULL) { client.SetCredentials(p.username, p.password); } switch (p.method) { case OrthancPluginHttpMethod_Get: client.SetMethod(HttpMethod_Get); break; case OrthancPluginHttpMethod_Post: client.SetMethod(HttpMethod_Post); client.GetBody().assign(p.body, p.bodySize); break; case OrthancPluginHttpMethod_Put: client.SetMethod(HttpMethod_Put); client.GetBody().assign(p.body, p.bodySize); break; case OrthancPluginHttpMethod_Delete: client.SetMethod(HttpMethod_Delete); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } std::string s; client.ApplyAndThrowException(s); if (p.method != OrthancPluginHttpMethod_Delete) { CopyToMemoryBuffer(*p.target, s); } } void OrthancPlugins::CallHttpClient2(const void* parameters) { const _OrthancPluginCallHttpClient2& p = *reinterpret_cast<const _OrthancPluginCallHttpClient2*>(parameters); HttpClient client; client.SetUrl(p.url); client.SetConvertHeadersToLowerCase(false); if (p.timeout != 0) { client.SetTimeout(p.timeout); } if (p.username != NULL && p.password != NULL) { client.SetCredentials(p.username, p.password); } if (p.certificateFile != NULL) { std::string certificate(p.certificateFile); std::string key, password; if (p.certificateKeyFile) { key.assign(p.certificateKeyFile); } if (p.certificateKeyPassword) { password.assign(p.certificateKeyPassword); } client.SetClientCertificate(certificate, key, password); } client.SetPkcs11Enabled(p.pkcs11 ? true : false); for (uint32_t i = 0; i < p.headersCount; i++) { if (p.headersKeys[i] == NULL || p.headersValues[i] == NULL) { throw OrthancException(ErrorCode_NullPointer); } client.AddHeader(p.headersKeys[i], p.headersValues[i]); } switch (p.method) { case OrthancPluginHttpMethod_Get: client.SetMethod(HttpMethod_Get); break; case OrthancPluginHttpMethod_Post: client.SetMethod(HttpMethod_Post); client.GetBody().assign(p.body, p.bodySize); break; case OrthancPluginHttpMethod_Put: client.SetMethod(HttpMethod_Put); client.GetBody().assign(p.body, p.bodySize); break; case OrthancPluginHttpMethod_Delete: client.SetMethod(HttpMethod_Delete); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } std::string body; HttpClient::HttpHeaders headers; bool success = client.Apply(body, headers); // The HTTP request has succeeded *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus()); if (!success) { HttpClient::ThrowException(client.GetLastStatus()); } // Copy the HTTP headers of the answer, if the plugin requested them if (p.answerHeaders != NULL) { Json::Value json = Json::objectValue; for (HttpClient::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); ++it) { json[it->first] = it->second; } std::string s = json.toStyledString(); CopyToMemoryBuffer(*p.answerHeaders, s); } // Copy the body of the answer if it makes sense if (p.method != OrthancPluginHttpMethod_Delete) { CopyToMemoryBuffer(*p.answerBody, body); } } void OrthancPlugins::CallPeerApi(const void* parameters) { const _OrthancPluginCallPeerApi& p = *reinterpret_cast<const _OrthancPluginCallPeerApi*>(parameters); const OrthancPeers& peers = *reinterpret_cast<const OrthancPeers*>(p.peers); HttpClient client(peers.GetPeerParameters(p.peerIndex), p.uri); client.SetConvertHeadersToLowerCase(false); if (p.timeout != 0) { client.SetTimeout(p.timeout); } for (uint32_t i = 0; i < p.additionalHeadersCount; i++) { if (p.additionalHeadersKeys[i] == NULL || p.additionalHeadersValues[i] == NULL) { throw OrthancException(ErrorCode_NullPointer); } client.AddHeader(p.additionalHeadersKeys[i], p.additionalHeadersValues[i]); } switch (p.method) { case OrthancPluginHttpMethod_Get: client.SetMethod(HttpMethod_Get); break; case OrthancPluginHttpMethod_Post: client.SetMethod(HttpMethod_Post); client.GetBody().assign(p.body, p.bodySize); break; case OrthancPluginHttpMethod_Put: client.SetMethod(HttpMethod_Put); client.GetBody().assign(p.body, p.bodySize); break; case OrthancPluginHttpMethod_Delete: client.SetMethod(HttpMethod_Delete); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } std::string body; HttpClient::HttpHeaders headers; bool success = client.Apply(body, headers); // The HTTP request has succeeded *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus()); if (!success) { HttpClient::ThrowException(client.GetLastStatus()); } // Copy the HTTP headers of the answer, if the plugin requested them if (p.answerHeaders != NULL) { Json::Value json = Json::objectValue; for (HttpClient::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); ++it) { json[it->first] = it->second; } std::string s = json.toStyledString(); CopyToMemoryBuffer(*p.answerHeaders, s); } // Copy the body of the answer if it makes sense if (p.method != OrthancPluginHttpMethod_Delete) { CopyToMemoryBuffer(*p.answerBody, body); } } void OrthancPlugins::ConvertPixelFormat(const void* parameters) { const _OrthancPluginConvertPixelFormat& p = *reinterpret_cast<const _OrthancPluginConvertPixelFormat*>(parameters); const ImageAccessor& source = *reinterpret_cast<const ImageAccessor*>(p.source); std::auto_ptr<ImageAccessor> target(new Image(Plugins::Convert(p.targetFormat), source.GetWidth(), source.GetHeight(), false)); ImageProcessing::Convert(*target, source); *(p.target) = ReturnImage(target); } void OrthancPlugins::GetFontInfo(const void* parameters) { const _OrthancPluginGetFontInfo& p = *reinterpret_cast<const _OrthancPluginGetFontInfo*>(parameters); { OrthancConfiguration::ReaderLock lock; const Font& font = lock.GetConfiguration().GetFontRegistry().GetFont(p.fontIndex); if (p.name != NULL) { *(p.name) = font.GetName().c_str(); } else if (p.size != NULL) { *(p.size) = font.GetSize(); } else { throw OrthancException(ErrorCode_InternalError); } } } void OrthancPlugins::DrawText(const void* parameters) { const _OrthancPluginDrawText& p = *reinterpret_cast<const _OrthancPluginDrawText*>(parameters); ImageAccessor& target = *reinterpret_cast<ImageAccessor*>(p.image); { OrthancConfiguration::ReaderLock lock; const Font& font = lock.GetConfiguration().GetFontRegistry().GetFont(p.fontIndex); font.Draw(target, p.utf8Text, p.x, p.y, p.r, p.g, p.b); } } void OrthancPlugins::ApplyDicomToJson(_OrthancPluginService service, const void* parameters) { const _OrthancPluginDicomToJson& p = *reinterpret_cast<const _OrthancPluginDicomToJson*>(parameters); std::auto_ptr<ParsedDicomFile> dicom; if (service == _OrthancPluginService_DicomBufferToJson) { dicom.reset(new ParsedDicomFile(p.buffer, p.size)); } else { if (p.instanceId == NULL) { throw OrthancException(ErrorCode_NullPointer); } std::string content; { PImpl::ServerContextLock lock(*pimpl_); lock.GetContext().ReadDicom(content, p.instanceId); } dicom.reset(new ParsedDicomFile(content)); } Json::Value json; dicom->DatasetToJson(json, Plugins::Convert(p.format), static_cast<DicomToJsonFlags>(p.flags), p.maxStringLength); Json::FastWriter writer; *p.result = CopyString(writer.write(json)); } void OrthancPlugins::ApplyCreateDicom(_OrthancPluginService service, const void* parameters) { const _OrthancPluginCreateDicom& p = *reinterpret_cast<const _OrthancPluginCreateDicom*>(parameters); Json::Value json; if (p.json == NULL) { json = Json::objectValue; } else { Json::Reader reader; if (!reader.parse(p.json, json)) { throw OrthancException(ErrorCode_BadJson); } } std::string dicom; { std::auto_ptr<ParsedDicomFile> file (ParsedDicomFile::CreateFromJson(json, static_cast<DicomFromJsonFlags>(p.flags))); if (p.pixelData) { file->EmbedImage(*reinterpret_cast<const ImageAccessor*>(p.pixelData)); } file->SaveToMemoryBuffer(dicom); } CopyToMemoryBuffer(*p.target, dicom); } void OrthancPlugins::ComputeHash(_OrthancPluginService service, const void* parameters) { const _OrthancPluginComputeHash& p = *reinterpret_cast<const _OrthancPluginComputeHash*>(parameters); std::string hash; switch (service) { case _OrthancPluginService_ComputeMd5: Toolbox::ComputeMD5(hash, p.buffer, p.size); break; case _OrthancPluginService_ComputeSha1: Toolbox::ComputeSHA1(hash, p.buffer, p.size); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } *p.result = CopyString(hash); } void OrthancPlugins::ApplyCreateImage(_OrthancPluginService service, const void* parameters) { const _OrthancPluginCreateImage& p = *reinterpret_cast<const _OrthancPluginCreateImage*>(parameters); std::auto_ptr<ImageAccessor> result; switch (service) { case _OrthancPluginService_CreateImage: result.reset(new Image(Plugins::Convert(p.format), p.width, p.height, false)); break; case _OrthancPluginService_CreateImageAccessor: result.reset(new ImageAccessor); result->AssignWritable(Plugins::Convert(p.format), p.width, p.height, p.pitch, p.buffer); break; case _OrthancPluginService_DecodeDicomImage: { result.reset(Decode(p.constBuffer, p.bufferSize, p.frameIndex)); break; } default: throw OrthancException(ErrorCode_InternalError); } *(p.target) = ReturnImage(result); } void OrthancPlugins::ApplySendMultipartItem(const void* parameters) { // An exception might be raised in this function if the // connection was closed by the HTTP client. const _OrthancPluginAnswerBuffer& p = *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters); HttpOutput& output = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); std::map<std::string, std::string> headers; // No custom headers output.SendMultipartItem(p.answer, p.answerSize, headers); } void OrthancPlugins::ApplySendMultipartItem2(const void* parameters) { // An exception might be raised in this function if the // connection was closed by the HTTP client. const _OrthancPluginSendMultipartItem2& p = *reinterpret_cast<const _OrthancPluginSendMultipartItem2*>(parameters); HttpOutput& output = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); std::map<std::string, std::string> headers; for (uint32_t i = 0; i < p.headersCount; i++) { headers[p.headersKeys[i]] = p.headersValues[i]; } output.SendMultipartItem(p.answer, p.answerSize, headers); } void OrthancPlugins::DatabaseAnswer(const void* parameters) { const _OrthancPluginDatabaseAnswer& p = *reinterpret_cast<const _OrthancPluginDatabaseAnswer*>(parameters); if (pimpl_->database_.get() != NULL) { pimpl_->database_->AnswerReceived(p); } else { throw OrthancException(ErrorCode_BadRequest, "Cannot invoke this service without a custom database back-end"); } } namespace { class DictionaryReadLocker { private: const DcmDataDictionary& dictionary_; public: DictionaryReadLocker() : dictionary_(dcmDataDict.rdlock()) { } ~DictionaryReadLocker() { dcmDataDict.unlock(); } const DcmDataDictionary* operator->() { return &dictionary_; } }; } void OrthancPlugins::ApplyLookupDictionary(const void* parameters) { const _OrthancPluginLookupDictionary& p = *reinterpret_cast<const _OrthancPluginLookupDictionary*>(parameters); DicomTag tag(FromDcmtkBridge::ParseTag(p.name)); DcmTagKey tag2(tag.GetGroup(), tag.GetElement()); DictionaryReadLocker locker; const DcmDictEntry* entry = locker->findEntry(tag2, NULL); if (entry == NULL) { throw OrthancException(ErrorCode_UnknownDicomTag); } else { p.target->group = entry->getKey().getGroup(); p.target->element = entry->getKey().getElement(); p.target->vr = Plugins::Convert(FromDcmtkBridge::Convert(entry->getEVR())); p.target->minMultiplicity = static_cast<uint32_t>(entry->getVMMin()); p.target->maxMultiplicity = (entry->getVMMax() == DcmVariableVM ? 0 : static_cast<uint32_t>(entry->getVMMax())); } } bool OrthancPlugins::InvokeSafeService(SharedLibrary& plugin, _OrthancPluginService service, const void* parameters) { // Services that can be run without mutual exclusion switch (service) { case _OrthancPluginService_GetOrthancPath: { std::string s = SystemToolbox::GetPathToExecutable(); *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s); return true; } case _OrthancPluginService_GetOrthancDirectory: { std::string s = SystemToolbox::GetDirectoryOfExecutable(); *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s); return true; } case _OrthancPluginService_GetConfigurationPath: { std::string s; { OrthancConfiguration::ReaderLock lock; s = lock.GetConfiguration().GetConfigurationAbsolutePath(); } *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s); return true; } case _OrthancPluginService_GetConfiguration: { std::string s; { OrthancConfiguration::ReaderLock lock; lock.GetConfiguration().Format(s); } *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s); return true; } case _OrthancPluginService_BufferCompression: BufferCompression(parameters); return true; case _OrthancPluginService_AnswerBuffer: AnswerBuffer(parameters); return true; case _OrthancPluginService_CompressAndAnswerPngImage: CompressAndAnswerPngImage(parameters); return true; case _OrthancPluginService_CompressAndAnswerImage: CompressAndAnswerImage(parameters); return true; case _OrthancPluginService_GetDicomForInstance: GetDicomForInstance(parameters); return true; case _OrthancPluginService_RestApiGet: RestApiGet(parameters, false); return true; case _OrthancPluginService_RestApiGetAfterPlugins: RestApiGet(parameters, true); return true; case _OrthancPluginService_RestApiGet2: RestApiGet2(parameters); return true; case _OrthancPluginService_RestApiPost: RestApiPostPut(true, parameters, false); return true; case _OrthancPluginService_RestApiPostAfterPlugins: RestApiPostPut(true, parameters, true); return true; case _OrthancPluginService_RestApiDelete: RestApiDelete(parameters, false); return true; case _OrthancPluginService_RestApiDeleteAfterPlugins: RestApiDelete(parameters, true); return true; case _OrthancPluginService_RestApiPut: RestApiPostPut(false, parameters, false); return true; case _OrthancPluginService_RestApiPutAfterPlugins: RestApiPostPut(false, parameters, true); return true; case _OrthancPluginService_Redirect: Redirect(parameters); return true; case _OrthancPluginService_SendUnauthorized: SendUnauthorized(parameters); return true; case _OrthancPluginService_SendMethodNotAllowed: SendMethodNotAllowed(parameters); return true; case _OrthancPluginService_SendHttpStatus: SendHttpStatus(parameters); return true; case _OrthancPluginService_SendHttpStatusCode: SendHttpStatusCode(parameters); return true; case _OrthancPluginService_SetCookie: SetCookie(parameters); return true; case _OrthancPluginService_SetHttpHeader: SetHttpHeader(parameters); return true; case _OrthancPluginService_SetHttpErrorDetails: SetHttpErrorDetails(parameters); return true; case _OrthancPluginService_LookupPatient: case _OrthancPluginService_LookupStudy: case _OrthancPluginService_LookupStudyWithAccessionNumber: case _OrthancPluginService_LookupSeries: case _OrthancPluginService_LookupInstance: LookupResource(service, parameters); return true; case _OrthancPluginService_GetInstanceRemoteAet: case _OrthancPluginService_GetInstanceSize: case _OrthancPluginService_GetInstanceData: case _OrthancPluginService_GetInstanceJson: case _OrthancPluginService_GetInstanceSimplifiedJson: case _OrthancPluginService_HasInstanceMetadata: case _OrthancPluginService_GetInstanceMetadata: case _OrthancPluginService_GetInstanceOrigin: AccessDicomInstance(service, parameters); return true; case _OrthancPluginService_SetGlobalProperty: { const _OrthancPluginGlobalProperty& p = *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters); if (p.property < 1024) { return false; } else { PImpl::ServerContextLock lock(*pimpl_); lock.GetContext().GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value); return true; } } case _OrthancPluginService_GetGlobalProperty: { const _OrthancPluginGlobalProperty& p = *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters); std::string result; { PImpl::ServerContextLock lock(*pimpl_); result = lock.GetContext().GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value); } *(p.result) = CopyString(result); return true; } case _OrthancPluginService_GetExpectedDatabaseVersion: { const _OrthancPluginReturnSingleValue& p = *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters); *(p.resultUint32) = ORTHANC_DATABASE_VERSION; return true; } case _OrthancPluginService_StartMultipartAnswer: { const _OrthancPluginStartMultipartAnswer& p = *reinterpret_cast<const _OrthancPluginStartMultipartAnswer*>(parameters); HttpOutput& output = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput(); output.StartMultipart(p.subType, p.contentType); return true; } case _OrthancPluginService_SendMultipartItem: ApplySendMultipartItem(parameters); return true; case _OrthancPluginService_SendMultipartItem2: ApplySendMultipartItem2(parameters); return true; case _OrthancPluginService_ReadFile: { const _OrthancPluginReadFile& p = *reinterpret_cast<const _OrthancPluginReadFile*>(parameters); std::string content; SystemToolbox::ReadFile(content, p.path); CopyToMemoryBuffer(*p.target, content.size() > 0 ? content.c_str() : NULL, content.size()); return true; } case _OrthancPluginService_WriteFile: { const _OrthancPluginWriteFile& p = *reinterpret_cast<const _OrthancPluginWriteFile*>(parameters); SystemToolbox::WriteFile(p.data, p.size, p.path); return true; } case _OrthancPluginService_GetErrorDescription: { const _OrthancPluginGetErrorDescription& p = *reinterpret_cast<const _OrthancPluginGetErrorDescription*>(parameters); *(p.target) = EnumerationToString(static_cast<ErrorCode>(p.error)); return true; } case _OrthancPluginService_GetImagePixelFormat: { const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters); *(p.resultPixelFormat) = Plugins::Convert(reinterpret_cast<const ImageAccessor*>(p.image)->GetFormat()); return true; } case _OrthancPluginService_GetImageWidth: { const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters); *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetWidth(); return true; } case _OrthancPluginService_GetImageHeight: { const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters); *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetHeight(); return true; } case _OrthancPluginService_GetImagePitch: { const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters); *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetPitch(); return true; } case _OrthancPluginService_GetImageBuffer: { const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters); *(p.resultBuffer) = reinterpret_cast<const ImageAccessor*>(p.image)->GetBuffer(); return true; } case _OrthancPluginService_FreeImage: { const _OrthancPluginFreeImage& p = *reinterpret_cast<const _OrthancPluginFreeImage*>(parameters); if (p.image != NULL) { delete reinterpret_cast<ImageAccessor*>(p.image); } return true; } case _OrthancPluginService_UncompressImage: UncompressImage(parameters); return true; case _OrthancPluginService_CompressImage: CompressImage(parameters); return true; case _OrthancPluginService_CallHttpClient: CallHttpClient(parameters); return true; case _OrthancPluginService_CallHttpClient2: CallHttpClient2(parameters); return true; case _OrthancPluginService_ConvertPixelFormat: ConvertPixelFormat(parameters); return true; case _OrthancPluginService_GetFontsCount: { const _OrthancPluginReturnSingleValue& p = *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters); { OrthancConfiguration::ReaderLock lock; *(p.resultUint32) = lock.GetConfiguration().GetFontRegistry().GetSize(); } return true; } case _OrthancPluginService_GetFontInfo: GetFontInfo(parameters); return true; case _OrthancPluginService_DrawText: DrawText(parameters); return true; case _OrthancPluginService_StorageAreaCreate: { const _OrthancPluginStorageAreaCreate& p = *reinterpret_cast<const _OrthancPluginStorageAreaCreate*>(parameters); IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea); storage.Create(p.uuid, p.content, static_cast<size_t>(p.size), Plugins::Convert(p.type)); return true; } case _OrthancPluginService_StorageAreaRead: { const _OrthancPluginStorageAreaRead& p = *reinterpret_cast<const _OrthancPluginStorageAreaRead*>(parameters); IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea); std::string content; storage.Read(content, p.uuid, Plugins::Convert(p.type)); CopyToMemoryBuffer(*p.target, content); return true; } case _OrthancPluginService_StorageAreaRemove: { const _OrthancPluginStorageAreaRemove& p = *reinterpret_cast<const _OrthancPluginStorageAreaRemove*>(parameters); IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea); storage.Remove(p.uuid, Plugins::Convert(p.type)); return true; } case _OrthancPluginService_DicomBufferToJson: case _OrthancPluginService_DicomInstanceToJson: ApplyDicomToJson(service, parameters); return true; case _OrthancPluginService_CreateDicom: ApplyCreateDicom(service, parameters); return true; case _OrthancPluginService_WorklistAddAnswer: { const _OrthancPluginWorklistAnswersOperation& p = *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters); reinterpret_cast<const WorklistHandler*>(p.query)->AddAnswer(p.answers, p.dicom, p.size); return true; } case _OrthancPluginService_WorklistMarkIncomplete: { const _OrthancPluginWorklistAnswersOperation& p = *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters); reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false); return true; } case _OrthancPluginService_WorklistIsMatch: { const _OrthancPluginWorklistQueryOperation& p = *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters); *p.isMatch = reinterpret_cast<const WorklistHandler*>(p.query)->IsMatch(p.dicom, p.size); return true; } case _OrthancPluginService_WorklistGetDicomQuery: { const _OrthancPluginWorklistQueryOperation& p = *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters); reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(*p.target); return true; } case _OrthancPluginService_FindAddAnswer: { const _OrthancPluginFindOperation& p = *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters); reinterpret_cast<DicomFindAnswers*>(p.answers)->Add(p.dicom, p.size); return true; } case _OrthancPluginService_FindMarkIncomplete: { const _OrthancPluginFindOperation& p = *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters); reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false); return true; } case _OrthancPluginService_GetFindQuerySize: case _OrthancPluginService_GetFindQueryTag: case _OrthancPluginService_GetFindQueryTagName: case _OrthancPluginService_GetFindQueryValue: { const _OrthancPluginFindOperation& p = *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters); reinterpret_cast<const FindHandler*>(p.query)->Invoke(service, p); return true; } case _OrthancPluginService_CreateImage: case _OrthancPluginService_CreateImageAccessor: case _OrthancPluginService_DecodeDicomImage: ApplyCreateImage(service, parameters); return true; case _OrthancPluginService_ComputeMd5: case _OrthancPluginService_ComputeSha1: ComputeHash(service, parameters); return true; case _OrthancPluginService_LookupDictionary: ApplyLookupDictionary(parameters); return true; case _OrthancPluginService_GenerateUuid: { *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(Toolbox::GenerateUuid()); return true; } case _OrthancPluginService_CreateFindMatcher: { const _OrthancPluginCreateFindMatcher& p = *reinterpret_cast<const _OrthancPluginCreateFindMatcher*>(parameters); ParsedDicomFile query(p.query, p.size); *(p.target) = reinterpret_cast<OrthancPluginFindMatcher*>(new HierarchicalMatcher(query)); return true; } case _OrthancPluginService_FreeFindMatcher: { const _OrthancPluginFreeFindMatcher& p = *reinterpret_cast<const _OrthancPluginFreeFindMatcher*>(parameters); if (p.matcher != NULL) { delete reinterpret_cast<HierarchicalMatcher*>(p.matcher); } return true; } case _OrthancPluginService_FindMatcherIsMatch: { const _OrthancPluginFindMatcherIsMatch& p = *reinterpret_cast<const _OrthancPluginFindMatcherIsMatch*>(parameters); if (p.matcher == NULL) { throw OrthancException(ErrorCode_NullPointer); } else { ParsedDicomFile query(p.dicom, p.size); *p.isMatch = reinterpret_cast<const HierarchicalMatcher*>(p.matcher)->Match(query) ? 1 : 0; return true; } } case _OrthancPluginService_GetPeers: { const _OrthancPluginGetPeers& p = *reinterpret_cast<const _OrthancPluginGetPeers*>(parameters); *(p.peers) = reinterpret_cast<OrthancPluginPeers*>(new OrthancPeers); return true; } case _OrthancPluginService_FreePeers: { const _OrthancPluginFreePeers& p = *reinterpret_cast<const _OrthancPluginFreePeers*>(parameters); if (p.peers != NULL) { delete reinterpret_cast<OrthancPeers*>(p.peers); } return true; } case _OrthancPluginService_GetPeersCount: { const _OrthancPluginGetPeersCount& p = *reinterpret_cast<const _OrthancPluginGetPeersCount*>(parameters); if (p.peers == NULL) { throw OrthancException(ErrorCode_NullPointer); } else { *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeersCount(); return true; } } case _OrthancPluginService_GetPeerName: { const _OrthancPluginGetPeerProperty& p = *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters); if (p.peers == NULL) { throw OrthancException(ErrorCode_NullPointer); } else { *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerName(p.peerIndex).c_str(); return true; } } case _OrthancPluginService_GetPeerUrl: { const _OrthancPluginGetPeerProperty& p = *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters); if (p.peers == NULL) { throw OrthancException(ErrorCode_NullPointer); } else { *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerParameters(p.peerIndex).GetUrl().c_str(); return true; } } case _OrthancPluginService_GetPeerUserProperty: { const _OrthancPluginGetPeerProperty& p = *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters); if (p.peers == NULL || p.userProperty == NULL) { throw OrthancException(ErrorCode_NullPointer); } else { const WebServiceParameters::Dictionary& properties = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerParameters(p.peerIndex).GetUserProperties(); WebServiceParameters::Dictionary::const_iterator found = properties.find(p.userProperty); if (found == properties.end()) { *(p.target) = NULL; } else { *(p.target) = found->second.c_str(); } return true; } } case _OrthancPluginService_CallPeerApi: CallPeerApi(parameters); return true; case _OrthancPluginService_CreateJob: { const _OrthancPluginCreateJob& p = *reinterpret_cast<const _OrthancPluginCreateJob*>(parameters); *(p.target) = reinterpret_cast<OrthancPluginJob*>(new PluginsJob(p)); return true; } case _OrthancPluginService_FreeJob: { const _OrthancPluginFreeJob& p = *reinterpret_cast<const _OrthancPluginFreeJob*>(parameters); if (p.job != NULL) { delete reinterpret_cast<PluginsJob*>(p.job); } return true; } case _OrthancPluginService_SubmitJob: { const _OrthancPluginSubmitJob& p = *reinterpret_cast<const _OrthancPluginSubmitJob*>(parameters); std::string uuid; PImpl::ServerContextLock lock(*pimpl_); lock.GetContext().GetJobsEngine().GetRegistry().Submit (uuid, reinterpret_cast<PluginsJob*>(p.job), p.priority); *p.resultId = CopyString(uuid); return true; } case _OrthancPluginService_AutodetectMimeType: { const _OrthancPluginRetrieveStaticString& p = *reinterpret_cast<const _OrthancPluginRetrieveStaticString*>(parameters); *p.result = EnumerationToString(SystemToolbox::AutodetectMimeType(p.argument)); return true; } default: return false; } } bool OrthancPlugins::InvokeProtectedService(SharedLibrary& plugin, _OrthancPluginService service, const void* parameters) { // Services that must be run in mutual exclusion. Guideline: // Whenever "pimpl_" is directly accessed by the service, it // should be listed here. switch (service) { case _OrthancPluginService_RegisterRestCallback: RegisterRestCallback(parameters, true); return true; case _OrthancPluginService_RegisterRestCallbackNoLock: RegisterRestCallback(parameters, false); return true; case _OrthancPluginService_RegisterOnStoredInstanceCallback: RegisterOnStoredInstanceCallback(parameters); return true; case _OrthancPluginService_RegisterOnChangeCallback: RegisterOnChangeCallback(parameters); return true; case _OrthancPluginService_RegisterWorklistCallback: RegisterWorklistCallback(parameters); return true; case _OrthancPluginService_RegisterFindCallback: RegisterFindCallback(parameters); return true; case _OrthancPluginService_RegisterMoveCallback: RegisterMoveCallback(parameters); return true; case _OrthancPluginService_RegisterDecodeImageCallback: RegisterDecodeImageCallback(parameters); return true; case _OrthancPluginService_RegisterJobsUnserializer: RegisterJobsUnserializer(parameters); return true; case _OrthancPluginService_RegisterIncomingHttpRequestFilter: RegisterIncomingHttpRequestFilter(parameters); return true; case _OrthancPluginService_RegisterIncomingHttpRequestFilter2: RegisterIncomingHttpRequestFilter2(parameters); return true; case _OrthancPluginService_RegisterStorageArea: { LOG(INFO) << "Plugin has registered a custom storage area"; const _OrthancPluginRegisterStorageArea& p = *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters); if (pimpl_->storageArea_.get() == NULL) { pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary())); } else { throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered); } return true; } case _OrthancPluginService_SetPluginProperty: { const _OrthancPluginSetPluginProperty& p = *reinterpret_cast<const _OrthancPluginSetPluginProperty*>(parameters); pimpl_->properties_[std::make_pair(p.plugin, p.property)] = p.value; return true; } case _OrthancPluginService_GetCommandLineArgumentsCount: { const _OrthancPluginReturnSingleValue& p = *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters); *(p.resultUint32) = pimpl_->argc_ - 1; return true; } case _OrthancPluginService_GetCommandLineArgument: { const _OrthancPluginGlobalProperty& p = *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters); if (p.property + 1 > pimpl_->argc_) { return false; } else { std::string arg = std::string(pimpl_->argv_[p.property + 1]); *(p.result) = CopyString(arg); return true; } } case _OrthancPluginService_RegisterDatabaseBackend: { LOG(INFO) << "Plugin has registered a custom database back-end"; const _OrthancPluginRegisterDatabaseBackend& p = *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters); if (pimpl_->database_.get() == NULL) { pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(), *p.backend, NULL, 0, p.payload)); } else { throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered); } *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get()); return true; } case _OrthancPluginService_RegisterDatabaseBackendV2: { LOG(INFO) << "Plugin has registered a custom database back-end"; const _OrthancPluginRegisterDatabaseBackendV2& p = *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters); if (pimpl_->database_.get() == NULL) { pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(), *p.backend, p.extensions, p.extensionsSize, p.payload)); } else { throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered); } *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get()); return true; } case _OrthancPluginService_DatabaseAnswer: throw OrthancException(ErrorCode_InternalError); // Implemented before locking (*) case _OrthancPluginService_RegisterErrorCode: { const _OrthancPluginRegisterErrorCode& p = *reinterpret_cast<const _OrthancPluginRegisterErrorCode*>(parameters); *(p.target) = pimpl_->dictionary_.Register(plugin, p.code, p.httpStatus, p.message); return true; } case _OrthancPluginService_RegisterDictionaryTag: { const _OrthancPluginRegisterDictionaryTag& p = *reinterpret_cast<const _OrthancPluginRegisterDictionaryTag*>(parameters); FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element), Plugins::Convert(p.vr), p.name, p.minMultiplicity, p.maxMultiplicity, ""); return true; } case _OrthancPluginService_RegisterPrivateDictionaryTag: { const _OrthancPluginRegisterPrivateDictionaryTag& p = *reinterpret_cast<const _OrthancPluginRegisterPrivateDictionaryTag*>(parameters); FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element), Plugins::Convert(p.vr), p.name, p.minMultiplicity, p.maxMultiplicity, p.privateCreator); return true; } case _OrthancPluginService_ReconstructMainDicomTags: { const _OrthancPluginReconstructMainDicomTags& p = *reinterpret_cast<const _OrthancPluginReconstructMainDicomTags*>(parameters); if (pimpl_->database_.get() == NULL) { throw OrthancException(ErrorCode_DatabasePlugin, "The service ReconstructMainDicomTags can only be invoked by custom database plugins"); } IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea); ServerToolbox::ReconstructMainDicomTags(*pimpl_->database_, storage, Plugins::Convert(p.level)); return true; } default: { // This service is unknown to the Orthanc plugin engine return false; } } } bool OrthancPlugins::InvokeService(SharedLibrary& plugin, _OrthancPluginService service, const void* parameters) { VLOG(1) << "Calling service " << service << " from plugin " << plugin.GetPath(); if (service == _OrthancPluginService_DatabaseAnswer) { // This case solves a deadlock at (*) reported by James Webster // on 2015-10-27 that was present in versions of Orthanc <= // 0.9.4 and related to database plugins implementing a custom // index. The problem was that locking the database is already // ensured by the "ServerIndex" class if the invoked service is // "DatabaseAnswer". DatabaseAnswer(parameters); return true; } if (InvokeSafeService(plugin, service, parameters)) { // The invoked service does not require locking return true; } else { // The invoked service requires locking boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); // (*) return InvokeProtectedService(plugin, service, parameters); } } bool OrthancPlugins::HasStorageArea() const { boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); return pimpl_->storageArea_.get() != NULL; } bool OrthancPlugins::HasDatabaseBackend() const { boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); return pimpl_->database_.get() != NULL; } IStorageArea* OrthancPlugins::CreateStorageArea() { if (!HasStorageArea()) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { return pimpl_->storageArea_->Create(); } } const SharedLibrary& OrthancPlugins::GetStorageAreaLibrary() const { if (!HasStorageArea()) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { return pimpl_->storageArea_->GetSharedLibrary(); } } IDatabaseWrapper& OrthancPlugins::GetDatabaseBackend() { if (!HasDatabaseBackend()) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { return *pimpl_->database_; } } const SharedLibrary& OrthancPlugins::GetDatabaseBackendLibrary() const { if (!HasDatabaseBackend()) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { return pimpl_->database_->GetSharedLibrary(); } } const char* OrthancPlugins::GetProperty(const char* plugin, _OrthancPluginProperty property) const { PImpl::Property p = std::make_pair(plugin, property); PImpl::Properties::const_iterator it = pimpl_->properties_.find(p); if (it == pimpl_->properties_.end()) { return NULL; } else { return it->second.c_str(); } } void OrthancPlugins::SetCommandLineArguments(int argc, char* argv[]) { if (argc < 1 || argv == NULL) { throw OrthancException(ErrorCode_ParameterOutOfRange); } pimpl_->argc_ = argc; pimpl_->argv_ = argv; } PluginsManager& OrthancPlugins::GetManager() { return pimpl_->manager_; } const PluginsManager& OrthancPlugins::GetManager() const { return pimpl_->manager_; } PluginsErrorDictionary& OrthancPlugins::GetErrorDictionary() { return pimpl_->dictionary_; } IWorklistRequestHandler* OrthancPlugins::ConstructWorklistRequestHandler() { if (HasWorklistHandler()) { return new WorklistHandler(*this); } else { return NULL; } } bool OrthancPlugins::HasWorklistHandler() { boost::mutex::scoped_lock lock(pimpl_->worklistCallbackMutex_); return pimpl_->worklistCallback_ != NULL; } IFindRequestHandler* OrthancPlugins::ConstructFindRequestHandler() { if (HasFindHandler()) { return new FindHandler(*this); } else { return NULL; } } bool OrthancPlugins::HasFindHandler() { boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_); return pimpl_->findCallback_ != NULL; } IMoveRequestHandler* OrthancPlugins::ConstructMoveRequestHandler() { if (HasMoveHandler()) { return new MoveHandler(*this); } else { return NULL; } } bool OrthancPlugins::HasMoveHandler() { boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); return pimpl_->moveCallbacks_.callback != NULL; } bool OrthancPlugins::HasCustomImageDecoder() { boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_); return !pimpl_->decodeImageCallbacks_.empty(); } ImageAccessor* OrthancPlugins::DecodeUnsafe(const void* dicom, size_t size, unsigned int frame) { boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_); for (PImpl::DecodeImageCallbacks::const_iterator decoder = pimpl_->decodeImageCallbacks_.begin(); decoder != pimpl_->decodeImageCallbacks_.end(); ++decoder) { OrthancPluginImage* pluginImage = NULL; if ((*decoder) (&pluginImage, dicom, size, frame) == OrthancPluginErrorCode_Success && pluginImage != NULL) { return reinterpret_cast<ImageAccessor*>(pluginImage); } } return NULL; } ImageAccessor* OrthancPlugins::Decode(const void* dicom, size_t size, unsigned int frame) { ImageAccessor* result = DecodeUnsafe(dicom, size, frame); if (result != NULL) { return result; } else { LOG(INFO) << "The installed image decoding plugins cannot handle an image, fallback to the built-in decoder"; DefaultDicomImageDecoder defaultDecoder; return defaultDecoder.Decode(dicom, size, frame); } } bool OrthancPlugins::IsAllowed(HttpMethod method, const char* uri, const char* ip, const char* username, const IHttpHandler::Arguments& httpHeaders, const IHttpHandler::GetArguments& getArguments) { OrthancPluginHttpMethod cMethod = Plugins::Convert(method); std::vector<const char*> httpKeys(httpHeaders.size()); std::vector<const char*> httpValues(httpHeaders.size()); size_t pos = 0; for (IHttpHandler::Arguments::const_iterator it = httpHeaders.begin(); it != httpHeaders.end(); ++it, pos++) { httpKeys[pos] = it->first.c_str(); httpValues[pos] = it->second.c_str(); } std::vector<const char*> getKeys(getArguments.size()); std::vector<const char*> getValues(getArguments.size()); for (size_t i = 0; i < getArguments.size(); i++) { getKeys[i] = getArguments[i].first.c_str(); getValues[i] = getArguments[i].second.c_str(); } // Improved callback with support for GET arguments, since Orthanc 1.3.0 for (PImpl::IncomingHttpRequestFilters2::const_iterator filter = pimpl_->incomingHttpRequestFilters2_.begin(); filter != pimpl_->incomingHttpRequestFilters2_.end(); ++filter) { int32_t allowed = (*filter) (cMethod, uri, ip, httpKeys.size(), httpKeys.empty() ? NULL : &httpKeys[0], httpValues.empty() ? NULL : &httpValues[0], getKeys.size(), getKeys.empty() ? NULL : &getKeys[0], getValues.empty() ? NULL : &getValues[0]); if (allowed == 0) { return false; } else if (allowed != 1) { // The callback is only allowed to answer 0 or 1 throw OrthancException(ErrorCode_Plugin); } } for (PImpl::IncomingHttpRequestFilters::const_iterator filter = pimpl_->incomingHttpRequestFilters_.begin(); filter != pimpl_->incomingHttpRequestFilters_.end(); ++filter) { int32_t allowed = (*filter) (cMethod, uri, ip, httpKeys.size(), httpKeys.empty() ? NULL : &httpKeys[0], httpValues.empty() ? NULL : &httpValues[0]); if (allowed == 0) { return false; } else if (allowed != 1) { // The callback is only allowed to answer 0 or 1 throw OrthancException(ErrorCode_Plugin); } } return true; } IJob* OrthancPlugins::UnserializeJob(const std::string& type, const Json::Value& value) { const std::string serialized = value.toStyledString(); boost::mutex::scoped_lock lock(pimpl_->jobsUnserializersMutex_); for (PImpl::JobsUnserializers::iterator unserializer = pimpl_->jobsUnserializers_.begin(); unserializer != pimpl_->jobsUnserializers_.end(); ++unserializer) { OrthancPluginJob* job = (*unserializer) (type.c_str(), serialized.c_str()); if (job != NULL) { return reinterpret_cast<PluginsJob*>(job); } } return NULL; } }