comparison Plugins/Engine/OrthancPlugins.cpp @ 2884:497a637366b4 db-changes

integration mainline->db-changes
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 12 Oct 2018 15:18:10 +0200
parents 7133ad478eea
children 9d277f8ad698
comparison
equal deleted inserted replaced
1762:2b91363cc1d1 2884:497a637366b4
1 /** 1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store 2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium 4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2018 Osimis S.A., Belgium
5 * 6 *
6 * This program is free software: you can redistribute it and/or 7 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as 8 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the 9 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version. 10 * License, or (at your option) any later version.
31 32
32 33
33 #include "../../OrthancServer/PrecompiledHeadersServer.h" 34 #include "../../OrthancServer/PrecompiledHeadersServer.h"
34 #include "OrthancPlugins.h" 35 #include "OrthancPlugins.h"
35 36
36 #if ORTHANC_PLUGINS_ENABLED != 1 37 #if ORTHANC_ENABLE_PLUGINS != 1
37 #error The plugin support is disabled 38 #error The plugin support is disabled
38 #endif 39 #endif
39 40
40 41
41 #include "../../Core/ChunkedBuffer.h" 42 #include "../../Core/ChunkedBuffer.h"
43 #include "../../Core/DicomFormat/DicomArray.h"
42 #include "../../Core/HttpServer/HttpToolbox.h" 44 #include "../../Core/HttpServer/HttpToolbox.h"
43 #include "../../Core/Logging.h" 45 #include "../../Core/Logging.h"
44 #include "../../Core/OrthancException.h" 46 #include "../../Core/OrthancException.h"
47 #include "../../Core/SerializationToolbox.h"
45 #include "../../Core/Toolbox.h" 48 #include "../../Core/Toolbox.h"
46 #include "../../OrthancServer/FromDcmtkBridge.h" 49 #include "../../Core/DicomParsing/FromDcmtkBridge.h"
50 #include "../../Core/DicomParsing/ToDcmtkBridge.h"
47 #include "../../OrthancServer/OrthancInitialization.h" 51 #include "../../OrthancServer/OrthancInitialization.h"
48 #include "../../OrthancServer/ServerContext.h" 52 #include "../../OrthancServer/ServerContext.h"
49 #include "../../OrthancServer/ServerToolbox.h" 53 #include "../../OrthancServer/ServerToolbox.h"
54 #include "../../OrthancServer/Search/HierarchicalMatcher.h"
55 #include "../../Core/DicomParsing/Internals/DicomImageDecoder.h"
50 #include "../../Core/Compression/ZlibCompressor.h" 56 #include "../../Core/Compression/ZlibCompressor.h"
51 #include "../../Core/Compression/GzipCompressor.h" 57 #include "../../Core/Compression/GzipCompressor.h"
52 #include "../../Core/Images/Image.h" 58 #include "../../Core/Images/Image.h"
53 #include "../../Core/Images/PngReader.h" 59 #include "../../Core/Images/PngReader.h"
54 #include "../../Core/Images/PngWriter.h" 60 #include "../../Core/Images/PngWriter.h"
55 #include "../../Core/Images/JpegReader.h" 61 #include "../../Core/Images/JpegReader.h"
56 #include "../../Core/Images/JpegWriter.h" 62 #include "../../Core/Images/JpegWriter.h"
57 #include "../../Core/Images/ImageProcessing.h" 63 #include "../../Core/Images/ImageProcessing.h"
64 #include "../../OrthancServer/DefaultDicomImageDecoder.h"
65 #include "../../OrthancServer/OrthancFindRequestHandler.h"
58 #include "PluginsEnumerations.h" 66 #include "PluginsEnumerations.h"
67 #include "PluginsJob.h"
59 68
60 #include <boost/regex.hpp> 69 #include <boost/regex.hpp>
70 #include <dcmtk/dcmdata/dcdict.h>
71 #include <dcmtk/dcmdata/dcdicent.h>
61 72
62 namespace Orthanc 73 namespace Orthanc
63 { 74 {
75 static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
76 const void* data,
77 size_t size)
78 {
79 target.size = size;
80
81 if (size == 0)
82 {
83 target.data = NULL;
84 }
85 else
86 {
87 target.data = malloc(size);
88 if (target.data != NULL)
89 {
90 memcpy(target.data, data, size);
91 }
92 else
93 {
94 throw OrthancException(ErrorCode_NotEnoughMemory);
95 }
96 }
97 }
98
99
100 static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
101 const std::string& str)
102 {
103 if (str.size() == 0)
104 {
105 target.size = 0;
106 target.data = NULL;
107 }
108 else
109 {
110 CopyToMemoryBuffer(target, str.c_str(), str.size());
111 }
112 }
113
114
115 static char* CopyString(const std::string& str)
116 {
117 char *result = reinterpret_cast<char*>(malloc(str.size() + 1));
118 if (result == NULL)
119 {
120 throw OrthancException(ErrorCode_NotEnoughMemory);
121 }
122
123 if (str.size() == 0)
124 {
125 result[0] = '\0';
126 }
127 else
128 {
129 memcpy(result, &str[0], str.size() + 1);
130 }
131
132 return result;
133 }
134
135
64 namespace 136 namespace
65 { 137 {
66 class PluginStorageArea : public IStorageArea 138 class PluginStorageArea : public IStorageArea
67 { 139 {
68 private: 140 private:
177 IStorageArea* Create() const 249 IStorageArea* Create() const
178 { 250 {
179 return new PluginStorageArea(callbacks_, errorDictionary_); 251 return new PluginStorageArea(callbacks_, errorDictionary_);
180 } 252 }
181 }; 253 };
182 } 254
183 255
184 256 class OrthancPeers : public boost::noncopyable
185 struct OrthancPlugins::PImpl 257 {
186 { 258 private:
259 std::vector<std::string> names_;
260 std::vector<WebServiceParameters> parameters_;
261
262 void CheckIndex(size_t i) const
263 {
264 assert(names_.size() == parameters_.size());
265 if (i >= names_.size())
266 {
267 throw OrthancException(ErrorCode_ParameterOutOfRange);
268 }
269 }
270
271 public:
272 OrthancPeers()
273 {
274 std::set<std::string> peers;
275 Configuration::GetListOfOrthancPeers(peers);
276
277 names_.reserve(peers.size());
278 parameters_.reserve(peers.size());
279
280 for (std::set<std::string>::const_iterator
281 it = peers.begin(); it != peers.end(); ++it)
282 {
283 WebServiceParameters peer;
284 if (Configuration::GetOrthancPeer(peer, *it))
285 {
286 names_.push_back(*it);
287 parameters_.push_back(peer);
288 }
289 }
290 }
291
292 size_t GetPeersCount() const
293 {
294 return names_.size();
295 }
296
297 const std::string& GetPeerName(size_t i) const
298 {
299 CheckIndex(i);
300 return names_[i];
301 }
302
303 const WebServiceParameters& GetPeerParameters(size_t i) const
304 {
305 CheckIndex(i);
306 return parameters_[i];
307 }
308 };
309 }
310
311
312 class OrthancPlugins::PImpl
313 {
314 private:
315 boost::mutex contextMutex_;
316 ServerContext* context_;
317
318
319 public:
187 class RestCallback : public boost::noncopyable 320 class RestCallback : public boost::noncopyable
188 { 321 {
189 private: 322 private:
190 boost::regex regex_; 323 boost::regex regex_;
191 OrthancPluginRestCallback callback_; 324 OrthancPluginRestCallback callback_;
231 } 364 }
232 } 365 }
233 }; 366 };
234 367
235 368
369 class ServerContextLock
370 {
371 private:
372 boost::mutex::scoped_lock lock_;
373 ServerContext* context_;
374
375 public:
376 ServerContextLock(PImpl& that) :
377 lock_(that.contextMutex_),
378 context_(that.context_)
379 {
380 if (context_ == NULL)
381 {
382 throw OrthancException(ErrorCode_DatabaseNotInitialized);
383 }
384 }
385
386 ServerContext& GetContext()
387 {
388 assert(context_ != NULL);
389 return *context_;
390 }
391 };
392
393
394 void SetServerContext(ServerContext* context)
395 {
396 boost::mutex::scoped_lock lock(contextMutex_);
397 context_ = context;
398 }
399
400
236 typedef std::pair<std::string, _OrthancPluginProperty> Property; 401 typedef std::pair<std::string, _OrthancPluginProperty> Property;
237 typedef std::list<RestCallback*> RestCallbacks; 402 typedef std::list<RestCallback*> RestCallbacks;
238 typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks; 403 typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks;
239 typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks; 404 typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks;
405 typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters;
406 typedef std::list<OrthancPluginIncomingHttpRequestFilter2> IncomingHttpRequestFilters2;
407 typedef std::list<OrthancPluginDecodeImageCallback> DecodeImageCallbacks;
408 typedef std::list<OrthancPluginJobsUnserializer> JobsUnserializers;
240 typedef std::map<Property, std::string> Properties; 409 typedef std::map<Property, std::string> Properties;
241 410
242 PluginsManager manager_; 411 PluginsManager manager_;
243 ServerContext* context_; 412
244 RestCallbacks restCallbacks_; 413 RestCallbacks restCallbacks_;
245 OnStoredCallbacks onStoredCallbacks_; 414 OnStoredCallbacks onStoredCallbacks_;
246 OnChangeCallbacks onChangeCallbacks_; 415 OnChangeCallbacks onChangeCallbacks_;
416 OrthancPluginFindCallback findCallback_;
417 OrthancPluginWorklistCallback worklistCallback_;
418 DecodeImageCallbacks decodeImageCallbacks_;
419 JobsUnserializers jobsUnserializers_;
420 _OrthancPluginMoveCallback moveCallbacks_;
421 IncomingHttpRequestFilters incomingHttpRequestFilters_;
422 IncomingHttpRequestFilters2 incomingHttpRequestFilters2_;
247 std::auto_ptr<StorageAreaFactory> storageArea_; 423 std::auto_ptr<StorageAreaFactory> storageArea_;
424
248 boost::recursive_mutex restCallbackMutex_; 425 boost::recursive_mutex restCallbackMutex_;
249 boost::recursive_mutex storedCallbackMutex_; 426 boost::recursive_mutex storedCallbackMutex_;
250 boost::recursive_mutex changeCallbackMutex_; 427 boost::recursive_mutex changeCallbackMutex_;
428 boost::mutex findCallbackMutex_;
429 boost::mutex worklistCallbackMutex_;
430 boost::mutex decodeImageCallbackMutex_;
431 boost::mutex jobsUnserializersMutex_;
251 boost::recursive_mutex invokeServiceMutex_; 432 boost::recursive_mutex invokeServiceMutex_;
433
252 Properties properties_; 434 Properties properties_;
253 int argc_; 435 int argc_;
254 char** argv_; 436 char** argv_;
255 std::auto_ptr<OrthancPluginDatabase> database_; 437 std::auto_ptr<OrthancPluginDatabase> database_;
256 PluginsErrorDictionary dictionary_; 438 PluginsErrorDictionary dictionary_;
257 439
258 PImpl() : 440 PImpl() :
259 context_(NULL), 441 context_(NULL),
442 findCallback_(NULL),
443 worklistCallback_(NULL),
260 argc_(1), 444 argc_(1),
261 argv_(NULL) 445 argv_(NULL)
262 { 446 {
447 memset(&moveCallbacks_, 0, sizeof(moveCallbacks_));
263 } 448 }
264 }; 449 };
265 450
266 451
267 452
268 static char* CopyString(const std::string& str) 453 class OrthancPlugins::WorklistHandler : public IWorklistRequestHandler
269 { 454 {
270 char *result = reinterpret_cast<char*>(malloc(str.size() + 1)); 455 private:
271 if (result == NULL) 456 OrthancPlugins& that_;
272 { 457 std::auto_ptr<HierarchicalMatcher> matcher_;
273 throw OrthancException(ErrorCode_NotEnoughMemory); 458 std::auto_ptr<ParsedDicomFile> filtered_;
274 } 459 ParsedDicomFile* currentQuery_;
275 460
276 if (str.size() == 0) 461 void Reset()
277 { 462 {
278 result[0] = '\0'; 463 matcher_.reset();
279 } 464 filtered_.reset();
280 else 465 currentQuery_ = NULL;
281 { 466 }
282 memcpy(result, &str[0], str.size() + 1); 467
283 } 468 public:
284 469 WorklistHandler(OrthancPlugins& that) : that_(that)
285 return result; 470 {
286 } 471 Reset();
472 }
473
474 virtual void Handle(DicomFindAnswers& answers,
475 ParsedDicomFile& query,
476 const std::string& remoteIp,
477 const std::string& remoteAet,
478 const std::string& calledAet,
479 ModalityManufacturer manufacturer)
480 {
481 static const char* LUA_CALLBACK = "IncomingWorklistRequestFilter";
482
483 {
484 PImpl::ServerContextLock lock(*that_.pimpl_);
485 LuaScripting::Lock lua(lock.GetContext().GetLuaScripting());
486
487 if (!lua.GetLua().IsExistingFunction(LUA_CALLBACK))
488 {
489 currentQuery_ = &query;
490 }
491 else
492 {
493 Json::Value source, origin;
494 query.DatasetToJson(source, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
495
496 OrthancFindRequestHandler::FormatOrigin
497 (origin, remoteIp, remoteAet, calledAet, manufacturer);
498
499 LuaFunctionCall call(lua.GetLua(), LUA_CALLBACK);
500 call.PushJson(source);
501 call.PushJson(origin);
502
503 Json::Value target;
504 call.ExecuteToJson(target, true);
505
506 filtered_.reset(ParsedDicomFile::CreateFromJson(target, DicomFromJsonFlags_None));
507 currentQuery_ = filtered_.get();
508 }
509 }
510
511 matcher_.reset(new HierarchicalMatcher(*currentQuery_));
512
513 {
514 boost::mutex::scoped_lock lock(that_.pimpl_->worklistCallbackMutex_);
515
516 if (that_.pimpl_->worklistCallback_)
517 {
518 OrthancPluginErrorCode error = that_.pimpl_->worklistCallback_
519 (reinterpret_cast<OrthancPluginWorklistAnswers*>(&answers),
520 reinterpret_cast<const OrthancPluginWorklistQuery*>(this),
521 remoteAet.c_str(),
522 calledAet.c_str());
523
524 if (error != OrthancPluginErrorCode_Success)
525 {
526 Reset();
527 that_.GetErrorDictionary().LogError(error, true);
528 throw OrthancException(static_cast<ErrorCode>(error));
529 }
530 }
531
532 Reset();
533 }
534 }
535
536 void GetDicomQuery(OrthancPluginMemoryBuffer& target) const
537 {
538 if (currentQuery_ == NULL)
539 {
540 throw OrthancException(ErrorCode_Plugin);
541 }
542
543 std::string dicom;
544 currentQuery_->SaveToMemoryBuffer(dicom);
545 CopyToMemoryBuffer(target, dicom.c_str(), dicom.size());
546 }
547
548 bool IsMatch(const void* dicom,
549 size_t size) const
550 {
551 if (matcher_.get() == NULL)
552 {
553 throw OrthancException(ErrorCode_Plugin);
554 }
555
556 ParsedDicomFile f(dicom, size);
557 return matcher_->Match(f);
558 }
559
560 void AddAnswer(OrthancPluginWorklistAnswers* answers,
561 const void* dicom,
562 size_t size) const
563 {
564 if (matcher_.get() == NULL)
565 {
566 throw OrthancException(ErrorCode_Plugin);
567 }
568
569 ParsedDicomFile f(dicom, size);
570 std::auto_ptr<ParsedDicomFile> summary(matcher_->Extract(f));
571 reinterpret_cast<DicomFindAnswers*>(answers)->Add(*summary);
572 }
573 };
574
575
576 class OrthancPlugins::FindHandler : public IFindRequestHandler
577 {
578 private:
579 OrthancPlugins& that_;
580 std::auto_ptr<DicomArray> currentQuery_;
581
582 void Reset()
583 {
584 currentQuery_.reset(NULL);
585 }
586
587 public:
588 FindHandler(OrthancPlugins& that) : that_(that)
589 {
590 Reset();
591 }
592
593 virtual void Handle(DicomFindAnswers& answers,
594 const DicomMap& input,
595 const std::list<DicomTag>& sequencesToReturn,
596 const std::string& remoteIp,
597 const std::string& remoteAet,
598 const std::string& calledAet,
599 ModalityManufacturer manufacturer)
600 {
601 DicomMap tmp;
602 tmp.Assign(input);
603
604 for (std::list<DicomTag>::const_iterator it = sequencesToReturn.begin();
605 it != sequencesToReturn.end(); ++it)
606 {
607 if (!input.HasTag(*it))
608 {
609 tmp.SetValue(*it, "", false);
610 }
611 }
612
613 {
614 boost::mutex::scoped_lock lock(that_.pimpl_->findCallbackMutex_);
615 currentQuery_.reset(new DicomArray(tmp));
616
617 if (that_.pimpl_->findCallback_)
618 {
619 OrthancPluginErrorCode error = that_.pimpl_->findCallback_
620 (reinterpret_cast<OrthancPluginFindAnswers*>(&answers),
621 reinterpret_cast<const OrthancPluginFindQuery*>(this),
622 remoteAet.c_str(),
623 calledAet.c_str());
624
625 if (error != OrthancPluginErrorCode_Success)
626 {
627 Reset();
628 that_.GetErrorDictionary().LogError(error, true);
629 throw OrthancException(static_cast<ErrorCode>(error));
630 }
631 }
632
633 Reset();
634 }
635 }
636
637 void Invoke(_OrthancPluginService service,
638 const _OrthancPluginFindOperation& operation) const
639 {
640 if (currentQuery_.get() == NULL)
641 {
642 throw OrthancException(ErrorCode_Plugin);
643 }
644
645 switch (service)
646 {
647 case _OrthancPluginService_GetFindQuerySize:
648 *operation.resultUint32 = currentQuery_->GetSize();
649 break;
650
651 case _OrthancPluginService_GetFindQueryTag:
652 {
653 const DicomTag& tag = currentQuery_->GetElement(operation.index).GetTag();
654 *operation.resultGroup = tag.GetGroup();
655 *operation.resultElement = tag.GetElement();
656 break;
657 }
658
659 case _OrthancPluginService_GetFindQueryTagName:
660 {
661 const DicomElement& element = currentQuery_->GetElement(operation.index);
662 *operation.resultString = CopyString(FromDcmtkBridge::GetTagName(element));
663 break;
664 }
665
666 case _OrthancPluginService_GetFindQueryValue:
667 {
668 *operation.resultString = CopyString(currentQuery_->GetElement(operation.index).GetValue().GetContent());
669 break;
670 }
671
672 default:
673 throw OrthancException(ErrorCode_InternalError);
674 }
675 }
676 };
677
678
679
680 class OrthancPlugins::MoveHandler : public IMoveRequestHandler
681 {
682 private:
683 class Driver : public IMoveRequestIterator
684 {
685 private:
686 void* driver_;
687 unsigned int count_;
688 unsigned int pos_;
689 OrthancPluginApplyMove apply_;
690 OrthancPluginFreeMove free_;
691
692 public:
693 Driver(void* driver,
694 unsigned int count,
695 OrthancPluginApplyMove apply,
696 OrthancPluginFreeMove free) :
697 driver_(driver),
698 count_(count),
699 pos_(0),
700 apply_(apply),
701 free_(free)
702 {
703 if (driver_ == NULL)
704 {
705 throw OrthancException(ErrorCode_Plugin);
706 }
707 }
708
709 virtual ~Driver()
710 {
711 if (driver_ != NULL)
712 {
713 free_(driver_);
714 driver_ = NULL;
715 }
716 }
717
718 virtual unsigned int GetSubOperationCount() const
719 {
720 return count_;
721 }
722
723 virtual Status DoNext()
724 {
725 if (pos_ >= count_)
726 {
727 throw OrthancException(ErrorCode_BadSequenceOfCalls);
728 }
729 else
730 {
731 OrthancPluginErrorCode error = apply_(driver_);
732 if (error != OrthancPluginErrorCode_Success)
733 {
734 LOG(ERROR) << "Error while doing C-Move from plugin: " << EnumerationToString(static_cast<ErrorCode>(error));
735 return Status_Failure;
736 }
737 else
738 {
739 pos_++;
740 return Status_Success;
741 }
742 }
743 }
744 };
745
746
747 _OrthancPluginMoveCallback params_;
748
749
750 static std::string ReadTag(const DicomMap& input,
751 const DicomTag& tag)
752 {
753 const DicomValue* value = input.TestAndGetValue(tag);
754 if (value != NULL &&
755 !value->IsBinary() &&
756 !value->IsNull())
757 {
758 return value->GetContent();
759 }
760 else
761 {
762 return std::string();
763 }
764 }
765
766
767
768 public:
769 MoveHandler(OrthancPlugins& that)
770 {
771 boost::recursive_mutex::scoped_lock lock(that.pimpl_->invokeServiceMutex_);
772 params_ = that.pimpl_->moveCallbacks_;
773
774 if (params_.callback == NULL ||
775 params_.getMoveSize == NULL ||
776 params_.applyMove == NULL ||
777 params_.freeMove == NULL)
778 {
779 throw OrthancException(ErrorCode_Plugin);
780 }
781 }
782
783 virtual IMoveRequestIterator* Handle(const std::string& targetAet,
784 const DicomMap& input,
785 const std::string& originatorIp,
786 const std::string& originatorAet,
787 const std::string& calledAet,
788 uint16_t originatorId)
789 {
790 std::string levelString = ReadTag(input, DICOM_TAG_QUERY_RETRIEVE_LEVEL);
791 std::string patientId = ReadTag(input, DICOM_TAG_PATIENT_ID);
792 std::string accessionNumber = ReadTag(input, DICOM_TAG_ACCESSION_NUMBER);
793 std::string studyInstanceUid = ReadTag(input, DICOM_TAG_STUDY_INSTANCE_UID);
794 std::string seriesInstanceUid = ReadTag(input, DICOM_TAG_SERIES_INSTANCE_UID);
795 std::string sopInstanceUid = ReadTag(input, DICOM_TAG_SOP_INSTANCE_UID);
796
797 OrthancPluginResourceType level = OrthancPluginResourceType_None;
798
799 if (!levelString.empty())
800 {
801 level = Plugins::Convert(StringToResourceType(levelString.c_str()));
802 }
803
804 void* driver = params_.callback(level,
805 patientId.empty() ? NULL : patientId.c_str(),
806 accessionNumber.empty() ? NULL : accessionNumber.c_str(),
807 studyInstanceUid.empty() ? NULL : studyInstanceUid.c_str(),
808 seriesInstanceUid.empty() ? NULL : seriesInstanceUid.c_str(),
809 sopInstanceUid.empty() ? NULL : sopInstanceUid.c_str(),
810 originatorAet.c_str(),
811 calledAet.c_str(),
812 targetAet.c_str(),
813 originatorId);
814
815 if (driver == NULL)
816 {
817 LOG(ERROR) << "Plugin cannot create a driver for an incoming C-MOVE request";
818 throw OrthancException(ErrorCode_Plugin);
819 }
820
821 unsigned int size = params_.getMoveSize(driver);
822
823 return new Driver(driver, size, params_.applyMove, params_.freeMove);
824 }
825 };
826
287 827
288 828
289 OrthancPlugins::OrthancPlugins() 829 OrthancPlugins::OrthancPlugins()
290 { 830 {
831 /* Sanity check of the compiler */
291 if (sizeof(int32_t) != sizeof(OrthancPluginErrorCode) || 832 if (sizeof(int32_t) != sizeof(OrthancPluginErrorCode) ||
292 sizeof(int32_t) != sizeof(OrthancPluginHttpMethod) || 833 sizeof(int32_t) != sizeof(OrthancPluginHttpMethod) ||
293 sizeof(int32_t) != sizeof(_OrthancPluginService) || 834 sizeof(int32_t) != sizeof(_OrthancPluginService) ||
294 sizeof(int32_t) != sizeof(_OrthancPluginProperty) || 835 sizeof(int32_t) != sizeof(_OrthancPluginProperty) ||
295 sizeof(int32_t) != sizeof(OrthancPluginPixelFormat) || 836 sizeof(int32_t) != sizeof(OrthancPluginPixelFormat) ||
299 sizeof(int32_t) != sizeof(OrthancPluginImageFormat) || 840 sizeof(int32_t) != sizeof(OrthancPluginImageFormat) ||
300 sizeof(int32_t) != sizeof(OrthancPluginCompressionType) || 841 sizeof(int32_t) != sizeof(OrthancPluginCompressionType) ||
301 sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation) || 842 sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation) ||
302 sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFlags) || 843 sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFlags) ||
303 sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFormat) || 844 sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFormat) ||
845 sizeof(int32_t) != sizeof(OrthancPluginCreateDicomFlags) ||
304 sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType) || 846 sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType) ||
305 sizeof(int32_t) != sizeof(OrthancPluginIdentifierConstraint) || 847 sizeof(int32_t) != sizeof(OrthancPluginIdentifierConstraint) ||
848 sizeof(int32_t) != sizeof(OrthancPluginInstanceOrigin) ||
849 sizeof(int32_t) != sizeof(OrthancPluginJobStepStatus) ||
306 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeBinary) != static_cast<int>(DicomToJsonFlags_IncludeBinary) || 850 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeBinary) != static_cast<int>(DicomToJsonFlags_IncludeBinary) ||
307 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePrivateTags) != static_cast<int>(DicomToJsonFlags_IncludePrivateTags) || 851 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePrivateTags) != static_cast<int>(DicomToJsonFlags_IncludePrivateTags) ||
308 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeUnknownTags) != static_cast<int>(DicomToJsonFlags_IncludeUnknownTags) || 852 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeUnknownTags) != static_cast<int>(DicomToJsonFlags_IncludeUnknownTags) ||
309 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePixelData) != static_cast<int>(DicomToJsonFlags_IncludePixelData) || 853 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePixelData) != static_cast<int>(DicomToJsonFlags_IncludePixelData) ||
310 static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToNull) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToNull) || 854 static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToNull) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToNull) ||
311 static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToAscii) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToAscii)) 855 static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToAscii) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToAscii) ||
312 { 856 static_cast<int>(OrthancPluginCreateDicomFlags_DecodeDataUriScheme) != static_cast<int>(DicomFromJsonFlags_DecodeDataUriScheme) ||
313 /* Sanity check of the compiler */ 857 static_cast<int>(OrthancPluginCreateDicomFlags_GenerateIdentifiers) != static_cast<int>(DicomFromJsonFlags_GenerateIdentifiers))
858
859 {
314 throw OrthancException(ErrorCode_Plugin); 860 throw OrthancException(ErrorCode_Plugin);
315 } 861 }
316 862
317 pimpl_.reset(new PImpl()); 863 pimpl_.reset(new PImpl());
318 pimpl_->manager_.RegisterServiceProvider(*this); 864 pimpl_->manager_.RegisterServiceProvider(*this);
319 } 865 }
320 866
321 867
322 void OrthancPlugins::SetServerContext(ServerContext& context) 868 void OrthancPlugins::SetServerContext(ServerContext& context)
323 { 869 {
324 pimpl_->context_ = &context; 870 pimpl_->SetServerContext(&context);
325 } 871 }
326 872
873
874 void OrthancPlugins::ResetServerContext()
875 {
876 pimpl_->SetServerContext(NULL);
877 }
327 878
328 879
329 OrthancPlugins::~OrthancPlugins() 880 OrthancPlugins::~OrthancPlugins()
330 { 881 {
331 for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin(); 882 for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin();
483 { 1034 {
484 return true; 1035 return true;
485 } 1036 }
486 else 1037 else
487 { 1038 {
488 GetErrorDictionary().LogError(error, true); 1039 GetErrorDictionary().LogError(error, false);
489 throw OrthancException(static_cast<ErrorCode>(error)); 1040 throw OrthancException(static_cast<ErrorCode>(error));
490 } 1041 }
491 } 1042 }
492 1043
493 1044
544 change.GetPublicId().c_str()); 1095 change.GetPublicId().c_str());
545 } 1096 }
546 1097
547 1098
548 1099
549 static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
550 const void* data,
551 size_t size)
552 {
553 target.size = size;
554
555 if (size == 0)
556 {
557 target.data = NULL;
558 }
559 else
560 {
561 target.data = malloc(size);
562 if (target.data != NULL)
563 {
564 memcpy(target.data, data, size);
565 }
566 else
567 {
568 throw OrthancException(ErrorCode_NotEnoughMemory);
569 }
570 }
571 }
572
573
574 static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
575 const std::string& str)
576 {
577 if (str.size() == 0)
578 {
579 target.size = 0;
580 target.data = NULL;
581 }
582 else
583 {
584 CopyToMemoryBuffer(target, str.c_str(), str.size());
585 }
586 }
587
588
589 void OrthancPlugins::RegisterRestCallback(const void* parameters, 1100 void OrthancPlugins::RegisterRestCallback(const void* parameters,
590 bool lock) 1101 bool lock)
591 { 1102 {
592 const _OrthancPluginRestCallback& p = 1103 const _OrthancPluginRestCallback& p =
593 *reinterpret_cast<const _OrthancPluginRestCallback*>(parameters); 1104 *reinterpret_cast<const _OrthancPluginRestCallback*>(parameters);
594 1105
595 LOG(INFO) << "Plugin has registered a REST callback " 1106 LOG(INFO) << "Plugin has registered a REST callback "
596 << (lock ? "with" : "witout") 1107 << (lock ? "with" : "without")
597 << " mutual exclusion on: " 1108 << " mutual exclusion on: "
598 << p.pathRegularExpression; 1109 << p.pathRegularExpression;
599 1110
600 pimpl_->restCallbacks_.push_back(new PImpl::RestCallback(p.pathRegularExpression, p.callback, lock)); 1111 pimpl_->restCallbacks_.push_back(new PImpl::RestCallback(p.pathRegularExpression, p.callback, lock));
601 } 1112 }
619 1130
620 LOG(INFO) << "Plugin has registered an OnChange callback"; 1131 LOG(INFO) << "Plugin has registered an OnChange callback";
621 pimpl_->onChangeCallbacks_.push_back(p.callback); 1132 pimpl_->onChangeCallbacks_.push_back(p.callback);
622 } 1133 }
623 1134
1135
1136 void OrthancPlugins::RegisterWorklistCallback(const void* parameters)
1137 {
1138 const _OrthancPluginWorklistCallback& p =
1139 *reinterpret_cast<const _OrthancPluginWorklistCallback*>(parameters);
1140
1141 boost::mutex::scoped_lock lock(pimpl_->worklistCallbackMutex_);
1142
1143 if (pimpl_->worklistCallback_ != NULL)
1144 {
1145 LOG(ERROR) << "Can only register one plugin to handle modality worklists";
1146 throw OrthancException(ErrorCode_Plugin);
1147 }
1148 else
1149 {
1150 LOG(INFO) << "Plugin has registered a callback to handle modality worklists";
1151 pimpl_->worklistCallback_ = p.callback;
1152 }
1153 }
1154
1155
1156 void OrthancPlugins::RegisterFindCallback(const void* parameters)
1157 {
1158 const _OrthancPluginFindCallback& p =
1159 *reinterpret_cast<const _OrthancPluginFindCallback*>(parameters);
1160
1161 boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_);
1162
1163 if (pimpl_->findCallback_ != NULL)
1164 {
1165 LOG(ERROR) << "Can only register one plugin to handle C-FIND requests";
1166 throw OrthancException(ErrorCode_Plugin);
1167 }
1168 else
1169 {
1170 LOG(INFO) << "Plugin has registered a callback to handle C-FIND requests";
1171 pimpl_->findCallback_ = p.callback;
1172 }
1173 }
1174
1175
1176 void OrthancPlugins::RegisterMoveCallback(const void* parameters)
1177 {
1178 // invokeServiceMutex_ is assumed to be locked
1179
1180 const _OrthancPluginMoveCallback& p =
1181 *reinterpret_cast<const _OrthancPluginMoveCallback*>(parameters);
1182
1183 if (pimpl_->moveCallbacks_.callback != NULL)
1184 {
1185 LOG(ERROR) << "Can only register one plugin to handle C-MOVE requests";
1186 throw OrthancException(ErrorCode_Plugin);
1187 }
1188 else
1189 {
1190 LOG(INFO) << "Plugin has registered a callback to handle C-MOVE requests";
1191 pimpl_->moveCallbacks_ = p;
1192 }
1193 }
1194
1195
1196 void OrthancPlugins::RegisterDecodeImageCallback(const void* parameters)
1197 {
1198 const _OrthancPluginDecodeImageCallback& p =
1199 *reinterpret_cast<const _OrthancPluginDecodeImageCallback*>(parameters);
1200
1201 boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
1202
1203 pimpl_->decodeImageCallbacks_.push_back(p.callback);
1204 LOG(INFO) << "Plugin has registered a callback to decode DICOM images ("
1205 << pimpl_->decodeImageCallbacks_.size() << " decoder(s) now active)";
1206 }
1207
1208
1209 void OrthancPlugins::RegisterJobsUnserializer(const void* parameters)
1210 {
1211 const _OrthancPluginJobsUnserializer& p =
1212 *reinterpret_cast<const _OrthancPluginJobsUnserializer*>(parameters);
1213
1214 boost::mutex::scoped_lock lock(pimpl_->jobsUnserializersMutex_);
1215
1216 pimpl_->jobsUnserializers_.push_back(p.unserializer);
1217 LOG(INFO) << "Plugin has registered a callback to unserialize jobs ("
1218 << pimpl_->jobsUnserializers_.size() << " unserializer(s) now active)";
1219 }
1220
1221
1222 void OrthancPlugins::RegisterIncomingHttpRequestFilter(const void* parameters)
1223 {
1224 const _OrthancPluginIncomingHttpRequestFilter& p =
1225 *reinterpret_cast<const _OrthancPluginIncomingHttpRequestFilter*>(parameters);
1226
1227 LOG(INFO) << "Plugin has registered a callback to filter incoming HTTP requests";
1228 pimpl_->incomingHttpRequestFilters_.push_back(p.callback);
1229 }
1230
1231
1232 void OrthancPlugins::RegisterIncomingHttpRequestFilter2(const void* parameters)
1233 {
1234 const _OrthancPluginIncomingHttpRequestFilter2& p =
1235 *reinterpret_cast<const _OrthancPluginIncomingHttpRequestFilter2*>(parameters);
1236
1237 LOG(INFO) << "Plugin has registered a callback to filter incoming HTTP requests";
1238 pimpl_->incomingHttpRequestFilters2_.push_back(p.callback);
1239 }
624 1240
625 1241
626 void OrthancPlugins::AnswerBuffer(const void* parameters) 1242 void OrthancPlugins::AnswerBuffer(const void* parameters)
627 { 1243 {
628 const _OrthancPluginAnswerBuffer& p = 1244 const _OrthancPluginAnswerBuffer& p =
770 1386
771 translatedOutput->Answer(compressed); 1387 translatedOutput->Answer(compressed);
772 } 1388 }
773 1389
774 1390
775 void OrthancPlugins::CheckContextAvailable()
776 {
777 if (!pimpl_->context_)
778 {
779 throw OrthancException(ErrorCode_DatabaseNotInitialized);
780 }
781 }
782
783
784 void OrthancPlugins::GetDicomForInstance(const void* parameters) 1391 void OrthancPlugins::GetDicomForInstance(const void* parameters)
785 { 1392 {
786 const _OrthancPluginGetDicomForInstance& p = 1393 const _OrthancPluginGetDicomForInstance& p =
787 *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters); 1394 *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters);
788 1395
789 std::string dicom; 1396 std::string dicom;
790 1397
791 CheckContextAvailable(); 1398 {
792 pimpl_->context_->ReadFile(dicom, p.instanceId, FileContentType_Dicom); 1399 PImpl::ServerContextLock lock(*pimpl_);
1400 lock.GetContext().ReadDicom(dicom, p.instanceId);
1401 }
793 1402
794 CopyToMemoryBuffer(*p.target, dicom); 1403 CopyToMemoryBuffer(*p.target, dicom);
795 } 1404 }
796 1405
797 1406
802 *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters); 1411 *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters);
803 1412
804 LOG(INFO) << "Plugin making REST GET call on URI " << p.uri 1413 LOG(INFO) << "Plugin making REST GET call on URI " << p.uri
805 << (afterPlugins ? " (after plugins)" : " (built-in API)"); 1414 << (afterPlugins ? " (after plugins)" : " (built-in API)");
806 1415
807 CheckContextAvailable(); 1416 IHttpHandler* handler;
808 IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); 1417
1418 {
1419 PImpl::ServerContextLock lock(*pimpl_);
1420 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
1421 }
809 1422
810 std::string result; 1423 std::string result;
811 if (HttpToolbox::SimpleGet(result, handler, RequestOrigin_Plugins, p.uri)) 1424 if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri))
812 { 1425 {
813 CopyToMemoryBuffer(*p.target, result); 1426 CopyToMemoryBuffer(*p.target, result);
814 } 1427 }
815 else 1428 else
816 { 1429 {
817 throw OrthancException(ErrorCode_BadRequest); 1430 throw OrthancException(ErrorCode_UnknownResource);
1431 }
1432 }
1433
1434
1435 void OrthancPlugins::RestApiGet2(const void* parameters)
1436 {
1437 const _OrthancPluginRestApiGet2& p =
1438 *reinterpret_cast<const _OrthancPluginRestApiGet2*>(parameters);
1439
1440 LOG(INFO) << "Plugin making REST GET call on URI " << p.uri
1441 << (p.afterPlugins ? " (after plugins)" : " (built-in API)");
1442
1443 IHttpHandler::Arguments headers;
1444
1445 for (uint32_t i = 0; i < p.headersCount; i++)
1446 {
1447 std::string name(p.headersKeys[i]);
1448 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
1449 headers[name] = p.headersValues[i];
1450 }
1451
1452 IHttpHandler* handler;
1453
1454 {
1455 PImpl::ServerContextLock lock(*pimpl_);
1456 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins);
1457 }
1458
1459 std::string result;
1460 if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri, headers))
1461 {
1462 CopyToMemoryBuffer(*p.target, result);
1463 }
1464 else
1465 {
1466 throw OrthancException(ErrorCode_UnknownResource);
818 } 1467 }
819 } 1468 }
820 1469
821 1470
822 void OrthancPlugins::RestApiPostPut(bool isPost, 1471 void OrthancPlugins::RestApiPostPut(bool isPost,
827 *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters); 1476 *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters);
828 1477
829 LOG(INFO) << "Plugin making REST " << EnumerationToString(isPost ? HttpMethod_Post : HttpMethod_Put) 1478 LOG(INFO) << "Plugin making REST " << EnumerationToString(isPost ? HttpMethod_Post : HttpMethod_Put)
830 << " call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)"); 1479 << " call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)");
831 1480
832 CheckContextAvailable(); 1481 IHttpHandler* handler;
833 IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); 1482
834 1483 {
1484 PImpl::ServerContextLock lock(*pimpl_);
1485 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
1486 }
1487
835 std::string result; 1488 std::string result;
836 if (isPost ? 1489 if (isPost ?
837 HttpToolbox::SimplePost(result, handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize) : 1490 HttpToolbox::SimplePost(result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize) :
838 HttpToolbox::SimplePut (result, handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize)) 1491 HttpToolbox::SimplePut (result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize))
839 { 1492 {
840 CopyToMemoryBuffer(*p.target, result); 1493 CopyToMemoryBuffer(*p.target, result);
841 } 1494 }
842 else 1495 else
843 { 1496 {
844 throw OrthancException(ErrorCode_BadRequest); 1497 throw OrthancException(ErrorCode_UnknownResource);
845 } 1498 }
846 } 1499 }
847 1500
848 1501
849 void OrthancPlugins::RestApiDelete(const void* parameters, 1502 void OrthancPlugins::RestApiDelete(const void* parameters,
851 { 1504 {
852 const char* uri = reinterpret_cast<const char*>(parameters); 1505 const char* uri = reinterpret_cast<const char*>(parameters);
853 LOG(INFO) << "Plugin making REST DELETE call on URI " << uri 1506 LOG(INFO) << "Plugin making REST DELETE call on URI " << uri
854 << (afterPlugins ? " (after plugins)" : " (built-in API)"); 1507 << (afterPlugins ? " (after plugins)" : " (built-in API)");
855 1508
856 CheckContextAvailable(); 1509 IHttpHandler* handler;
857 IHttpHandler& handler = pimpl_->context_->GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins); 1510
858 1511 {
859 if (!HttpToolbox::SimpleDelete(handler, RequestOrigin_Plugins, uri)) 1512 PImpl::ServerContextLock lock(*pimpl_);
860 { 1513 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
861 throw OrthancException(ErrorCode_BadRequest); 1514 }
1515
1516 if (!HttpToolbox::SimpleDelete(*handler, RequestOrigin_Plugins, uri))
1517 {
1518 throw OrthancException(ErrorCode_UnknownResource);
862 } 1519 }
863 } 1520 }
864 1521
865 1522
866 void OrthancPlugins::LookupResource(_OrthancPluginService service, 1523 void OrthancPlugins::LookupResource(_OrthancPluginService service,
907 1564
908 default: 1565 default:
909 throw OrthancException(ErrorCode_InternalError); 1566 throw OrthancException(ErrorCode_InternalError);
910 } 1567 }
911 1568
912 CheckContextAvailable();
913
914 std::list<std::string> result; 1569 std::list<std::string> result;
915 pimpl_->context_->GetIndex().LookupIdentifierExact(result, level, tag, p.argument); 1570
1571 {
1572 PImpl::ServerContextLock lock(*pimpl_);
1573 lock.GetContext().GetIndex().LookupIdentifierExact(result, level, tag, p.argument);
1574 }
916 1575
917 if (result.size() == 1) 1576 if (result.size() == 1)
918 { 1577 {
919 *p.result = CopyString(result.front()); 1578 *p.result = CopyString(result.front());
920 } 1579 }
989 *reinterpret_cast<DicomInstanceToStore*>(p.instance); 1648 *reinterpret_cast<DicomInstanceToStore*>(p.instance);
990 1649
991 switch (service) 1650 switch (service)
992 { 1651 {
993 case _OrthancPluginService_GetInstanceRemoteAet: 1652 case _OrthancPluginService_GetInstanceRemoteAet:
994 *p.resultString = instance.GetRemoteAet(); 1653 *p.resultString = instance.GetOrigin().GetRemoteAetC();
995 return; 1654 return;
996 1655
997 case _OrthancPluginService_GetInstanceSize: 1656 case _OrthancPluginService_GetInstanceSize:
998 *p.resultInt64 = instance.GetBufferSize(); 1657 *p.resultInt64 = instance.GetBufferSize();
999 return; 1658 return;
1021 s = writer.write(instance.GetJson()); 1680 s = writer.write(instance.GetJson());
1022 } 1681 }
1023 else 1682 else
1024 { 1683 {
1025 Json::Value simplified; 1684 Json::Value simplified;
1026 Toolbox::SimplifyTags(simplified, instance.GetJson()); 1685 ServerToolbox::SimplifyTags(simplified, instance.GetJson(), DicomToJsonFormat_Human);
1027 s = writer.write(simplified); 1686 s = writer.write(simplified);
1028 } 1687 }
1029 1688
1030 *p.resultStringToFree = CopyString(s); 1689 *p.resultStringToFree = CopyString(s);
1031 return; 1690 return;
1032 } 1691 }
1692
1693 case _OrthancPluginService_GetInstanceOrigin: // New in Orthanc 0.9.5
1694 *p.resultOrigin = Plugins::Convert(instance.GetOrigin().GetRequestOrigin());
1695 return;
1033 1696
1034 default: 1697 default:
1035 throw OrthancException(ErrorCode_InternalError); 1698 throw OrthancException(ErrorCode_InternalError);
1036 } 1699 }
1037 } 1700 }
1093 1756
1094 CopyToMemoryBuffer(*p.target, result); 1757 CopyToMemoryBuffer(*p.target, result);
1095 } 1758 }
1096 1759
1097 1760
1761 static OrthancPluginImage* ReturnImage(std::auto_ptr<ImageAccessor>& image)
1762 {
1763 // Images returned to plugins are assumed to be writeable. If the
1764 // input image is read-only, we return a copy so that it can be modified.
1765
1766 if (image->IsReadOnly())
1767 {
1768 std::auto_ptr<Image> copy(new Image(image->GetFormat(), image->GetWidth(), image->GetHeight(), false));
1769 ImageProcessing::Copy(*copy, *image);
1770 image.reset(NULL);
1771 return reinterpret_cast<OrthancPluginImage*>(copy.release());
1772 }
1773 else
1774 {
1775 return reinterpret_cast<OrthancPluginImage*>(image.release());
1776 }
1777 }
1778
1779
1098 void OrthancPlugins::UncompressImage(const void* parameters) 1780 void OrthancPlugins::UncompressImage(const void* parameters)
1099 { 1781 {
1100 const _OrthancPluginUncompressImage& p = *reinterpret_cast<const _OrthancPluginUncompressImage*>(parameters); 1782 const _OrthancPluginUncompressImage& p = *reinterpret_cast<const _OrthancPluginUncompressImage*>(parameters);
1101 1783
1102 std::auto_ptr<ImageAccessor> image; 1784 std::auto_ptr<ImageAccessor> image;
1115 image.reset(new JpegReader); 1797 image.reset(new JpegReader);
1116 reinterpret_cast<JpegReader&>(*image).ReadFromMemory(p.data, p.size); 1798 reinterpret_cast<JpegReader&>(*image).ReadFromMemory(p.data, p.size);
1117 break; 1799 break;
1118 } 1800 }
1119 1801
1802 case OrthancPluginImageFormat_Dicom:
1803 {
1804 image.reset(Decode(p.data, p.size, 0));
1805 break;
1806 }
1807
1120 default: 1808 default:
1121 throw OrthancException(ErrorCode_ParameterOutOfRange); 1809 throw OrthancException(ErrorCode_ParameterOutOfRange);
1122 } 1810 }
1123 1811
1124 *(p.target) = reinterpret_cast<OrthancPluginImage*>(image.release()); 1812 *(p.target) = ReturnImage(image);
1125 } 1813 }
1126 1814
1127 1815
1128 void OrthancPlugins::CompressImage(const void* parameters) 1816 void OrthancPlugins::CompressImage(const void* parameters)
1129 { 1817 {
1130 const _OrthancPluginCompressImage& p = *reinterpret_cast<const _OrthancPluginCompressImage*>(parameters); 1818 const _OrthancPluginCompressImage& p = *reinterpret_cast<const _OrthancPluginCompressImage*>(parameters);
1131 1819
1132 std::string compressed; 1820 std::string compressed;
1133 1821
1822 ImageAccessor accessor;
1823 accessor.AssignReadOnly(Plugins::Convert(p.pixelFormat), p.width, p.height, p.pitch, p.buffer);
1824
1134 switch (p.imageFormat) 1825 switch (p.imageFormat)
1135 { 1826 {
1136 case OrthancPluginImageFormat_Png: 1827 case OrthancPluginImageFormat_Png:
1137 { 1828 {
1138 PngWriter writer; 1829 PngWriter writer;
1139 writer.WriteToMemory(compressed, p.width, p.height, p.pitch, Plugins::Convert(p.pixelFormat), p.buffer); 1830 writer.WriteToMemory(compressed, accessor);
1140 break; 1831 break;
1141 } 1832 }
1142 1833
1143 case OrthancPluginImageFormat_Jpeg: 1834 case OrthancPluginImageFormat_Jpeg:
1144 { 1835 {
1145 JpegWriter writer; 1836 JpegWriter writer;
1146 writer.SetQuality(p.quality); 1837 writer.SetQuality(p.quality);
1147 writer.WriteToMemory(compressed, p.width, p.height, p.pitch, Plugins::Convert(p.pixelFormat), p.buffer); 1838 writer.WriteToMemory(compressed, accessor);
1148 break; 1839 break;
1149 } 1840 }
1150 1841
1151 default: 1842 default:
1152 throw OrthancException(ErrorCode_ParameterOutOfRange); 1843 throw OrthancException(ErrorCode_ParameterOutOfRange);
1201 CopyToMemoryBuffer(*p.target, s); 1892 CopyToMemoryBuffer(*p.target, s);
1202 } 1893 }
1203 } 1894 }
1204 1895
1205 1896
1897 void OrthancPlugins::CallHttpClient2(const void* parameters)
1898 {
1899 const _OrthancPluginCallHttpClient2& p = *reinterpret_cast<const _OrthancPluginCallHttpClient2*>(parameters);
1900
1901 HttpClient client;
1902 client.SetUrl(p.url);
1903 client.SetConvertHeadersToLowerCase(false);
1904
1905 if (p.timeout != 0)
1906 {
1907 client.SetTimeout(p.timeout);
1908 }
1909
1910 if (p.username != NULL &&
1911 p.password != NULL)
1912 {
1913 client.SetCredentials(p.username, p.password);
1914 }
1915
1916 if (p.certificateFile != NULL)
1917 {
1918 std::string certificate(p.certificateFile);
1919 std::string key, password;
1920
1921 if (p.certificateKeyFile)
1922 {
1923 key.assign(p.certificateKeyFile);
1924 }
1925
1926 if (p.certificateKeyPassword)
1927 {
1928 password.assign(p.certificateKeyPassword);
1929 }
1930
1931 client.SetClientCertificate(certificate, key, password);
1932 }
1933
1934 client.SetPkcs11Enabled(p.pkcs11 ? true : false);
1935
1936 for (uint32_t i = 0; i < p.headersCount; i++)
1937 {
1938 if (p.headersKeys[i] == NULL ||
1939 p.headersValues[i] == NULL)
1940 {
1941 throw OrthancException(ErrorCode_NullPointer);
1942 }
1943
1944 client.AddHeader(p.headersKeys[i], p.headersValues[i]);
1945 }
1946
1947 switch (p.method)
1948 {
1949 case OrthancPluginHttpMethod_Get:
1950 client.SetMethod(HttpMethod_Get);
1951 break;
1952
1953 case OrthancPluginHttpMethod_Post:
1954 client.SetMethod(HttpMethod_Post);
1955 client.GetBody().assign(p.body, p.bodySize);
1956 break;
1957
1958 case OrthancPluginHttpMethod_Put:
1959 client.SetMethod(HttpMethod_Put);
1960 client.GetBody().assign(p.body, p.bodySize);
1961 break;
1962
1963 case OrthancPluginHttpMethod_Delete:
1964 client.SetMethod(HttpMethod_Delete);
1965 break;
1966
1967 default:
1968 throw OrthancException(ErrorCode_ParameterOutOfRange);
1969 }
1970
1971 std::string body;
1972 HttpClient::HttpHeaders headers;
1973
1974 bool success = client.Apply(body, headers);
1975
1976 // The HTTP request has succeeded
1977 *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus());
1978
1979 if (!success)
1980 {
1981 HttpClient::ThrowException(client.GetLastStatus());
1982 }
1983
1984 // Copy the HTTP headers of the answer, if the plugin requested them
1985 if (p.answerHeaders != NULL)
1986 {
1987 Json::Value json = Json::objectValue;
1988
1989 for (HttpClient::HttpHeaders::const_iterator
1990 it = headers.begin(); it != headers.end(); ++it)
1991 {
1992 json[it->first] = it->second;
1993 }
1994
1995 std::string s = json.toStyledString();
1996 CopyToMemoryBuffer(*p.answerHeaders, s);
1997 }
1998
1999 // Copy the body of the answer if it makes sense
2000 if (p.method != OrthancPluginHttpMethod_Delete)
2001 {
2002 CopyToMemoryBuffer(*p.answerBody, body);
2003 }
2004 }
2005
2006
2007 void OrthancPlugins::CallPeerApi(const void* parameters)
2008 {
2009 const _OrthancPluginCallPeerApi& p = *reinterpret_cast<const _OrthancPluginCallPeerApi*>(parameters);
2010 const OrthancPeers& peers = *reinterpret_cast<const OrthancPeers*>(p.peers);
2011
2012 HttpClient client(peers.GetPeerParameters(p.peerIndex), p.uri);
2013 client.SetConvertHeadersToLowerCase(false);
2014
2015 if (p.timeout != 0)
2016 {
2017 client.SetTimeout(p.timeout);
2018 }
2019
2020 for (uint32_t i = 0; i < p.additionalHeadersCount; i++)
2021 {
2022 if (p.additionalHeadersKeys[i] == NULL ||
2023 p.additionalHeadersValues[i] == NULL)
2024 {
2025 throw OrthancException(ErrorCode_NullPointer);
2026 }
2027
2028 client.AddHeader(p.additionalHeadersKeys[i], p.additionalHeadersValues[i]);
2029 }
2030
2031 switch (p.method)
2032 {
2033 case OrthancPluginHttpMethod_Get:
2034 client.SetMethod(HttpMethod_Get);
2035 break;
2036
2037 case OrthancPluginHttpMethod_Post:
2038 client.SetMethod(HttpMethod_Post);
2039 client.GetBody().assign(p.body, p.bodySize);
2040 break;
2041
2042 case OrthancPluginHttpMethod_Put:
2043 client.SetMethod(HttpMethod_Put);
2044 client.GetBody().assign(p.body, p.bodySize);
2045 break;
2046
2047 case OrthancPluginHttpMethod_Delete:
2048 client.SetMethod(HttpMethod_Delete);
2049 break;
2050
2051 default:
2052 throw OrthancException(ErrorCode_ParameterOutOfRange);
2053 }
2054
2055 std::string body;
2056 HttpClient::HttpHeaders headers;
2057
2058 bool success = client.Apply(body, headers);
2059
2060 // The HTTP request has succeeded
2061 *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus());
2062
2063 if (!success)
2064 {
2065 HttpClient::ThrowException(client.GetLastStatus());
2066 }
2067
2068 // Copy the HTTP headers of the answer, if the plugin requested them
2069 if (p.answerHeaders != NULL)
2070 {
2071 Json::Value json = Json::objectValue;
2072
2073 for (HttpClient::HttpHeaders::const_iterator
2074 it = headers.begin(); it != headers.end(); ++it)
2075 {
2076 json[it->first] = it->second;
2077 }
2078
2079 std::string s = json.toStyledString();
2080 CopyToMemoryBuffer(*p.answerHeaders, s);
2081 }
2082
2083 // Copy the body of the answer if it makes sense
2084 if (p.method != OrthancPluginHttpMethod_Delete)
2085 {
2086 CopyToMemoryBuffer(*p.answerBody, body);
2087 }
2088 }
2089
2090
1206 void OrthancPlugins::ConvertPixelFormat(const void* parameters) 2091 void OrthancPlugins::ConvertPixelFormat(const void* parameters)
1207 { 2092 {
1208 const _OrthancPluginConvertPixelFormat& p = *reinterpret_cast<const _OrthancPluginConvertPixelFormat*>(parameters); 2093 const _OrthancPluginConvertPixelFormat& p = *reinterpret_cast<const _OrthancPluginConvertPixelFormat*>(parameters);
1209 const ImageAccessor& source = *reinterpret_cast<const ImageAccessor*>(p.source); 2094 const ImageAccessor& source = *reinterpret_cast<const ImageAccessor*>(p.source);
1210 2095
1211 std::auto_ptr<ImageAccessor> target(new Image(Plugins::Convert(p.targetFormat), source.GetWidth(), source.GetHeight())); 2096 std::auto_ptr<ImageAccessor> target(new Image(Plugins::Convert(p.targetFormat), source.GetWidth(), source.GetHeight(), false));
1212 ImageProcessing::Convert(*target, source); 2097 ImageProcessing::Convert(*target, source);
1213 2098
1214 *(p.target) = reinterpret_cast<OrthancPluginImage*>(target.release()); 2099 *(p.target) = ReturnImage(target);
1215 } 2100 }
1216 2101
1217 2102
1218 2103
1219 void OrthancPlugins::GetFontInfo(const void* parameters) 2104 void OrthancPlugins::GetFontInfo(const void* parameters)
1262 } 2147 }
1263 else 2148 else
1264 { 2149 {
1265 if (p.instanceId == NULL) 2150 if (p.instanceId == NULL)
1266 { 2151 {
1267 throw OrthancException(ErrorCode_ParameterOutOfRange); 2152 throw OrthancException(ErrorCode_NullPointer);
1268 } 2153 }
1269 2154
1270 std::string content; 2155 std::string content;
1271 pimpl_->context_->ReadFile(content, p.instanceId, FileContentType_Dicom); 2156
2157 {
2158 PImpl::ServerContextLock lock(*pimpl_);
2159 lock.GetContext().ReadDicom(content, p.instanceId);
2160 }
2161
1272 dicom.reset(new ParsedDicomFile(content)); 2162 dicom.reset(new ParsedDicomFile(content));
1273 } 2163 }
1274 2164
1275 Json::Value json; 2165 Json::Value json;
1276 dicom->ToJson(json, Plugins::Convert(p.format), 2166 dicom->DatasetToJson(json, Plugins::Convert(p.format),
1277 static_cast<DicomToJsonFlags>(p.flags), p.maxStringLength); 2167 static_cast<DicomToJsonFlags>(p.flags), p.maxStringLength);
1278 2168
1279 Json::FastWriter writer; 2169 Json::FastWriter writer;
1280 *p.result = CopyString(writer.write(json)); 2170 *p.result = CopyString(writer.write(json));
1281 } 2171 }
1282 2172
1283 2173
1284 bool OrthancPlugins::InvokeService(SharedLibrary& plugin, 2174 void OrthancPlugins::ApplyCreateDicom(_OrthancPluginService service,
1285 _OrthancPluginService service, 2175 const void* parameters)
1286 const void* parameters) 2176 {
1287 { 2177 const _OrthancPluginCreateDicom& p =
1288 VLOG(1) << "Calling service " << service << " from plugin " << plugin.GetPath(); 2178 *reinterpret_cast<const _OrthancPluginCreateDicom*>(parameters);
1289 2179
1290 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); 2180 Json::Value json;
1291 2181
2182 if (p.json == NULL)
2183 {
2184 json = Json::objectValue;
2185 }
2186 else
2187 {
2188 Json::Reader reader;
2189 if (!reader.parse(p.json, json))
2190 {
2191 throw OrthancException(ErrorCode_BadJson);
2192 }
2193 }
2194
2195 std::string dicom;
2196
2197 {
2198 std::auto_ptr<ParsedDicomFile> file
2199 (ParsedDicomFile::CreateFromJson(json, static_cast<DicomFromJsonFlags>(p.flags)));
2200
2201 if (p.pixelData)
2202 {
2203 file->EmbedImage(*reinterpret_cast<const ImageAccessor*>(p.pixelData));
2204 }
2205
2206 file->SaveToMemoryBuffer(dicom);
2207 }
2208
2209 CopyToMemoryBuffer(*p.target, dicom);
2210 }
2211
2212
2213 void OrthancPlugins::ComputeHash(_OrthancPluginService service,
2214 const void* parameters)
2215 {
2216 const _OrthancPluginComputeHash& p =
2217 *reinterpret_cast<const _OrthancPluginComputeHash*>(parameters);
2218
2219 std::string hash;
1292 switch (service) 2220 switch (service)
1293 { 2221 {
2222 case _OrthancPluginService_ComputeMd5:
2223 Toolbox::ComputeMD5(hash, p.buffer, p.size);
2224 break;
2225
2226 case _OrthancPluginService_ComputeSha1:
2227 Toolbox::ComputeSHA1(hash, p.buffer, p.size);
2228 break;
2229
2230 default:
2231 throw OrthancException(ErrorCode_ParameterOutOfRange);
2232 }
2233
2234 *p.result = CopyString(hash);
2235 }
2236
2237
2238 void OrthancPlugins::ApplyCreateImage(_OrthancPluginService service,
2239 const void* parameters)
2240 {
2241 const _OrthancPluginCreateImage& p =
2242 *reinterpret_cast<const _OrthancPluginCreateImage*>(parameters);
2243
2244 std::auto_ptr<ImageAccessor> result;
2245
2246 switch (service)
2247 {
2248 case _OrthancPluginService_CreateImage:
2249 result.reset(new Image(Plugins::Convert(p.format), p.width, p.height, false));
2250 break;
2251
2252 case _OrthancPluginService_CreateImageAccessor:
2253 result.reset(new ImageAccessor);
2254 result->AssignWritable(Plugins::Convert(p.format), p.width, p.height, p.pitch, p.buffer);
2255 break;
2256
2257 case _OrthancPluginService_DecodeDicomImage:
2258 {
2259 result.reset(Decode(p.constBuffer, p.bufferSize, p.frameIndex));
2260 break;
2261 }
2262
2263 default:
2264 throw OrthancException(ErrorCode_InternalError);
2265 }
2266
2267 *(p.target) = ReturnImage(result);
2268 }
2269
2270
2271 void OrthancPlugins::ApplySendMultipartItem(const void* parameters)
2272 {
2273 // An exception might be raised in this function if the
2274 // connection was closed by the HTTP client.
2275 const _OrthancPluginAnswerBuffer& p =
2276 *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters);
2277
2278 HttpOutput* output = reinterpret_cast<HttpOutput*>(p.output);
2279
2280 std::map<std::string, std::string> headers; // No custom headers
2281 output->SendMultipartItem(p.answer, p.answerSize, headers);
2282 }
2283
2284
2285 void OrthancPlugins::ApplySendMultipartItem2(const void* parameters)
2286 {
2287 // An exception might be raised in this function if the
2288 // connection was closed by the HTTP client.
2289 const _OrthancPluginSendMultipartItem2& p =
2290 *reinterpret_cast<const _OrthancPluginSendMultipartItem2*>(parameters);
2291 HttpOutput* output = reinterpret_cast<HttpOutput*>(p.output);
2292
2293 std::map<std::string, std::string> headers;
2294 for (uint32_t i = 0; i < p.headersCount; i++)
2295 {
2296 headers[p.headersKeys[i]] = p.headersValues[i];
2297 }
2298
2299 output->SendMultipartItem(p.answer, p.answerSize, headers);
2300 }
2301
2302
2303 void OrthancPlugins::DatabaseAnswer(const void* parameters)
2304 {
2305 const _OrthancPluginDatabaseAnswer& p =
2306 *reinterpret_cast<const _OrthancPluginDatabaseAnswer*>(parameters);
2307
2308 if (pimpl_->database_.get() != NULL)
2309 {
2310 pimpl_->database_->AnswerReceived(p);
2311 }
2312 else
2313 {
2314 LOG(ERROR) << "Cannot invoke this service without a custom database back-end";
2315 throw OrthancException(ErrorCode_BadRequest);
2316 }
2317 }
2318
2319
2320 namespace
2321 {
2322 class DictionaryReadLocker
2323 {
2324 private:
2325 const DcmDataDictionary& dictionary_;
2326
2327 public:
2328 DictionaryReadLocker() : dictionary_(dcmDataDict.rdlock())
2329 {
2330 }
2331
2332 ~DictionaryReadLocker()
2333 {
2334 dcmDataDict.unlock();
2335 }
2336
2337 const DcmDataDictionary* operator->()
2338 {
2339 return &dictionary_;
2340 }
2341 };
2342 }
2343
2344
2345 void OrthancPlugins::ApplyLookupDictionary(const void* parameters)
2346 {
2347 const _OrthancPluginLookupDictionary& p =
2348 *reinterpret_cast<const _OrthancPluginLookupDictionary*>(parameters);
2349
2350 DicomTag tag(FromDcmtkBridge::ParseTag(p.name));
2351 DcmTagKey tag2(tag.GetGroup(), tag.GetElement());
2352
2353 DictionaryReadLocker locker;
2354 const DcmDictEntry* entry = locker->findEntry(tag2, NULL);
2355
2356 if (entry == NULL)
2357 {
2358 throw OrthancException(ErrorCode_UnknownDicomTag);
2359 }
2360 else
2361 {
2362 p.target->group = entry->getKey().getGroup();
2363 p.target->element = entry->getKey().getElement();
2364 p.target->vr = Plugins::Convert(FromDcmtkBridge::Convert(entry->getEVR()));
2365 p.target->minMultiplicity = static_cast<uint32_t>(entry->getVMMin());
2366 p.target->maxMultiplicity = (entry->getVMMax() == DcmVariableVM ? 0 : static_cast<uint32_t>(entry->getVMMax()));
2367 }
2368 }
2369
2370
2371 bool OrthancPlugins::InvokeSafeService(SharedLibrary& plugin,
2372 _OrthancPluginService service,
2373 const void* parameters)
2374 {
2375 // Services that can be run without mutual exclusion
2376
2377 switch (service)
2378 {
1294 case _OrthancPluginService_GetOrthancPath: 2379 case _OrthancPluginService_GetOrthancPath:
1295 { 2380 {
1296 std::string s = Toolbox::GetPathToExecutable(); 2381 std::string s = SystemToolbox::GetPathToExecutable();
1297 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s); 2382 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
1298 return true; 2383 return true;
1299 } 2384 }
1300 2385
1301 case _OrthancPluginService_GetOrthancDirectory: 2386 case _OrthancPluginService_GetOrthancDirectory:
1302 { 2387 {
1303 std::string s = Toolbox::GetDirectoryOfExecutable(); 2388 std::string s = SystemToolbox::GetDirectoryOfExecutable();
1304 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s); 2389 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
1305 return true; 2390 return true;
1306 } 2391 }
1307 2392
1308 case _OrthancPluginService_GetConfigurationPath: 2393 case _OrthancPluginService_GetConfigurationPath:
1323 2408
1324 case _OrthancPluginService_BufferCompression: 2409 case _OrthancPluginService_BufferCompression:
1325 BufferCompression(parameters); 2410 BufferCompression(parameters);
1326 return true; 2411 return true;
1327 2412
1328 case _OrthancPluginService_RegisterRestCallback:
1329 RegisterRestCallback(parameters, true);
1330 return true;
1331
1332 case _OrthancPluginService_RegisterRestCallbackNoLock:
1333 RegisterRestCallback(parameters, false);
1334 return true;
1335
1336 case _OrthancPluginService_RegisterOnStoredInstanceCallback:
1337 RegisterOnStoredInstanceCallback(parameters);
1338 return true;
1339
1340 case _OrthancPluginService_RegisterOnChangeCallback:
1341 RegisterOnChangeCallback(parameters);
1342 return true;
1343
1344 case _OrthancPluginService_AnswerBuffer: 2413 case _OrthancPluginService_AnswerBuffer:
1345 AnswerBuffer(parameters); 2414 AnswerBuffer(parameters);
1346 return true; 2415 return true;
1347 2416
1348 case _OrthancPluginService_CompressAndAnswerPngImage: 2417 case _OrthancPluginService_CompressAndAnswerPngImage:
1361 RestApiGet(parameters, false); 2430 RestApiGet(parameters, false);
1362 return true; 2431 return true;
1363 2432
1364 case _OrthancPluginService_RestApiGetAfterPlugins: 2433 case _OrthancPluginService_RestApiGetAfterPlugins:
1365 RestApiGet(parameters, true); 2434 RestApiGet(parameters, true);
2435 return true;
2436
2437 case _OrthancPluginService_RestApiGet2:
2438 RestApiGet2(parameters);
1366 return true; 2439 return true;
1367 2440
1368 case _OrthancPluginService_RestApiPost: 2441 case _OrthancPluginService_RestApiPost:
1369 RestApiPostPut(true, parameters, false); 2442 RestApiPostPut(true, parameters, false);
1370 return true; 2443 return true;
1430 case _OrthancPluginService_GetInstanceData: 2503 case _OrthancPluginService_GetInstanceData:
1431 case _OrthancPluginService_GetInstanceJson: 2504 case _OrthancPluginService_GetInstanceJson:
1432 case _OrthancPluginService_GetInstanceSimplifiedJson: 2505 case _OrthancPluginService_GetInstanceSimplifiedJson:
1433 case _OrthancPluginService_HasInstanceMetadata: 2506 case _OrthancPluginService_HasInstanceMetadata:
1434 case _OrthancPluginService_GetInstanceMetadata: 2507 case _OrthancPluginService_GetInstanceMetadata:
2508 case _OrthancPluginService_GetInstanceOrigin:
1435 AccessDicomInstance(service, parameters); 2509 AccessDicomInstance(service, parameters);
1436 return true; 2510 return true;
1437
1438 case _OrthancPluginService_RegisterStorageArea:
1439 {
1440 LOG(INFO) << "Plugin has registered a custom storage area";
1441 const _OrthancPluginRegisterStorageArea& p =
1442 *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
1443
1444 if (pimpl_->storageArea_.get() == NULL)
1445 {
1446 pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary()));
1447 }
1448 else
1449 {
1450 throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered);
1451 }
1452
1453 return true;
1454 }
1455
1456 case _OrthancPluginService_SetPluginProperty:
1457 {
1458 const _OrthancPluginSetPluginProperty& p =
1459 *reinterpret_cast<const _OrthancPluginSetPluginProperty*>(parameters);
1460 pimpl_->properties_[std::make_pair(p.plugin, p.property)] = p.value;
1461 return true;
1462 }
1463 2511
1464 case _OrthancPluginService_SetGlobalProperty: 2512 case _OrthancPluginService_SetGlobalProperty:
1465 { 2513 {
1466 const _OrthancPluginGlobalProperty& p = 2514 const _OrthancPluginGlobalProperty& p =
1467 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters); 2515 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
1469 { 2517 {
1470 return false; 2518 return false;
1471 } 2519 }
1472 else 2520 else
1473 { 2521 {
1474 CheckContextAvailable(); 2522 PImpl::ServerContextLock lock(*pimpl_);
1475 pimpl_->context_->GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value); 2523 lock.GetContext().GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
1476 return true; 2524 return true;
1477 } 2525 }
1478 } 2526 }
1479 2527
1480 case _OrthancPluginService_GetGlobalProperty: 2528 case _OrthancPluginService_GetGlobalProperty:
1481 { 2529 {
1482 CheckContextAvailable();
1483
1484 const _OrthancPluginGlobalProperty& p = 2530 const _OrthancPluginGlobalProperty& p =
1485 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters); 2531 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
1486 std::string result = pimpl_->context_->GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value); 2532
2533 std::string result;
2534
2535 {
2536 PImpl::ServerContextLock lock(*pimpl_);
2537 result = lock.GetContext().GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
2538 }
2539
1487 *(p.result) = CopyString(result); 2540 *(p.result) = CopyString(result);
1488 return true; 2541 return true;
1489 }
1490
1491 case _OrthancPluginService_GetCommandLineArgumentsCount:
1492 {
1493 const _OrthancPluginReturnSingleValue& p =
1494 *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
1495 *(p.resultUint32) = pimpl_->argc_ - 1;
1496 return true;
1497 }
1498
1499 case _OrthancPluginService_GetCommandLineArgument:
1500 {
1501 const _OrthancPluginGlobalProperty& p =
1502 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
1503
1504 if (p.property + 1 > pimpl_->argc_)
1505 {
1506 return false;
1507 }
1508 else
1509 {
1510 std::string arg = std::string(pimpl_->argv_[p.property + 1]);
1511 *(p.result) = CopyString(arg);
1512 return true;
1513 }
1514 }
1515
1516 case _OrthancPluginService_RegisterDatabaseBackend:
1517 {
1518 LOG(INFO) << "Plugin has registered a custom database back-end";
1519
1520 const _OrthancPluginRegisterDatabaseBackend& p =
1521 *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
1522
1523 if (pimpl_->database_.get() == NULL)
1524 {
1525 pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
1526 *p.backend, NULL, 0, p.payload));
1527 }
1528 else
1529 {
1530 throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
1531 }
1532
1533 *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
1534
1535 return true;
1536 }
1537
1538 case _OrthancPluginService_RegisterDatabaseBackendV2:
1539 {
1540 LOG(INFO) << "Plugin has registered a custom database back-end";
1541
1542 const _OrthancPluginRegisterDatabaseBackendV2& p =
1543 *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters);
1544
1545 if (pimpl_->database_.get() == NULL)
1546 {
1547 pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
1548 *p.backend, p.extensions,
1549 p.extensionsSize, p.payload));
1550 }
1551 else
1552 {
1553 throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
1554 }
1555
1556 *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
1557
1558 return true;
1559 }
1560
1561 case _OrthancPluginService_DatabaseAnswer:
1562 {
1563 const _OrthancPluginDatabaseAnswer& p =
1564 *reinterpret_cast<const _OrthancPluginDatabaseAnswer*>(parameters);
1565
1566 if (pimpl_->database_.get() != NULL)
1567 {
1568 pimpl_->database_->AnswerReceived(p);
1569 return true;
1570 }
1571 else
1572 {
1573 LOG(ERROR) << "Cannot invoke this service without a custom database back-end";
1574 throw OrthancException(ErrorCode_BadRequest);
1575 }
1576 } 2542 }
1577 2543
1578 case _OrthancPluginService_GetExpectedDatabaseVersion: 2544 case _OrthancPluginService_GetExpectedDatabaseVersion:
1579 { 2545 {
1580 const _OrthancPluginReturnSingleValue& p = 2546 const _OrthancPluginReturnSingleValue& p =
1591 output->StartMultipart(p.subType, p.contentType); 2557 output->StartMultipart(p.subType, p.contentType);
1592 return true; 2558 return true;
1593 } 2559 }
1594 2560
1595 case _OrthancPluginService_SendMultipartItem: 2561 case _OrthancPluginService_SendMultipartItem:
1596 { 2562 ApplySendMultipartItem(parameters);
1597 // An exception might be raised in this function if the 2563 return true;
1598 // connection was closed by the HTTP client. 2564
1599 const _OrthancPluginAnswerBuffer& p = 2565 case _OrthancPluginService_SendMultipartItem2:
1600 *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters); 2566 ApplySendMultipartItem2(parameters);
1601 HttpOutput* output = reinterpret_cast<HttpOutput*>(p.output); 2567 return true;
1602 output->SendMultipartItem(p.answer, p.answerSize);
1603 return true;
1604 }
1605 2568
1606 case _OrthancPluginService_ReadFile: 2569 case _OrthancPluginService_ReadFile:
1607 { 2570 {
1608 const _OrthancPluginReadFile& p = 2571 const _OrthancPluginReadFile& p =
1609 *reinterpret_cast<const _OrthancPluginReadFile*>(parameters); 2572 *reinterpret_cast<const _OrthancPluginReadFile*>(parameters);
1610 2573
1611 std::string content; 2574 std::string content;
1612 Toolbox::ReadFile(content, p.path); 2575 SystemToolbox::ReadFile(content, p.path);
1613 CopyToMemoryBuffer(*p.target, content.size() > 0 ? content.c_str() : NULL, content.size()); 2576 CopyToMemoryBuffer(*p.target, content.size() > 0 ? content.c_str() : NULL, content.size());
1614 2577
1615 return true; 2578 return true;
1616 } 2579 }
1617 2580
1618 case _OrthancPluginService_WriteFile: 2581 case _OrthancPluginService_WriteFile:
1619 { 2582 {
1620 const _OrthancPluginWriteFile& p = 2583 const _OrthancPluginWriteFile& p =
1621 *reinterpret_cast<const _OrthancPluginWriteFile*>(parameters); 2584 *reinterpret_cast<const _OrthancPluginWriteFile*>(parameters);
1622 Toolbox::WriteFile(p.data, p.size, p.path); 2585 SystemToolbox::WriteFile(p.data, p.size, p.path);
1623 return true; 2586 return true;
1624 } 2587 }
1625 2588
1626 case _OrthancPluginService_GetErrorDescription: 2589 case _OrthancPluginService_GetErrorDescription:
1627 { 2590 {
1660 } 2623 }
1661 2624
1662 case _OrthancPluginService_GetImageBuffer: 2625 case _OrthancPluginService_GetImageBuffer:
1663 { 2626 {
1664 const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters); 2627 const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
1665 *(p.resultBuffer) = reinterpret_cast<const ImageAccessor*>(p.image)->GetConstBuffer(); 2628 *(p.resultBuffer) = reinterpret_cast<const ImageAccessor*>(p.image)->GetBuffer();
1666 return true; 2629 return true;
1667 } 2630 }
1668 2631
1669 case _OrthancPluginService_FreeImage: 2632 case _OrthancPluginService_FreeImage:
1670 { 2633 {
1671 const _OrthancPluginFreeImage& p = *reinterpret_cast<const _OrthancPluginFreeImage*>(parameters); 2634 const _OrthancPluginFreeImage& p = *reinterpret_cast<const _OrthancPluginFreeImage*>(parameters);
1672 if (p.image == NULL) 2635
1673 { 2636 if (p.image != NULL)
1674 throw OrthancException(ErrorCode_ParameterOutOfRange);
1675 }
1676 else
1677 { 2637 {
1678 delete reinterpret_cast<ImageAccessor*>(p.image); 2638 delete reinterpret_cast<ImageAccessor*>(p.image);
1679 return true; 2639 }
1680 } 2640
2641 return true;
1681 } 2642 }
1682 2643
1683 case _OrthancPluginService_UncompressImage: 2644 case _OrthancPluginService_UncompressImage:
1684 UncompressImage(parameters); 2645 UncompressImage(parameters);
1685 return true; 2646 return true;
1688 CompressImage(parameters); 2649 CompressImage(parameters);
1689 return true; 2650 return true;
1690 2651
1691 case _OrthancPluginService_CallHttpClient: 2652 case _OrthancPluginService_CallHttpClient:
1692 CallHttpClient(parameters); 2653 CallHttpClient(parameters);
2654 return true;
2655
2656 case _OrthancPluginService_CallHttpClient2:
2657 CallHttpClient2(parameters);
1693 return true; 2658 return true;
1694 2659
1695 case _OrthancPluginService_ConvertPixelFormat: 2660 case _OrthancPluginService_ConvertPixelFormat:
1696 ConvertPixelFormat(parameters); 2661 ConvertPixelFormat(parameters);
1697 return true; 2662 return true;
1715 case _OrthancPluginService_StorageAreaCreate: 2680 case _OrthancPluginService_StorageAreaCreate:
1716 { 2681 {
1717 const _OrthancPluginStorageAreaCreate& p = 2682 const _OrthancPluginStorageAreaCreate& p =
1718 *reinterpret_cast<const _OrthancPluginStorageAreaCreate*>(parameters); 2683 *reinterpret_cast<const _OrthancPluginStorageAreaCreate*>(parameters);
1719 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea); 2684 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
1720 storage.Create(p.uuid, p.content, p.size, Plugins::Convert(p.type)); 2685 storage.Create(p.uuid, p.content, static_cast<size_t>(p.size), Plugins::Convert(p.type));
1721 return true; 2686 return true;
1722 } 2687 }
1723 2688
1724 case _OrthancPluginService_StorageAreaRead: 2689 case _OrthancPluginService_StorageAreaRead:
1725 { 2690 {
1739 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea); 2704 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
1740 storage.Remove(p.uuid, Plugins::Convert(p.type)); 2705 storage.Remove(p.uuid, Plugins::Convert(p.type));
1741 return true; 2706 return true;
1742 } 2707 }
1743 2708
2709 case _OrthancPluginService_DicomBufferToJson:
2710 case _OrthancPluginService_DicomInstanceToJson:
2711 ApplyDicomToJson(service, parameters);
2712 return true;
2713
2714 case _OrthancPluginService_CreateDicom:
2715 ApplyCreateDicom(service, parameters);
2716 return true;
2717
2718 case _OrthancPluginService_WorklistAddAnswer:
2719 {
2720 const _OrthancPluginWorklistAnswersOperation& p =
2721 *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
2722 reinterpret_cast<const WorklistHandler*>(p.query)->AddAnswer(p.answers, p.dicom, p.size);
2723 return true;
2724 }
2725
2726 case _OrthancPluginService_WorklistMarkIncomplete:
2727 {
2728 const _OrthancPluginWorklistAnswersOperation& p =
2729 *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
2730 reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
2731 return true;
2732 }
2733
2734 case _OrthancPluginService_WorklistIsMatch:
2735 {
2736 const _OrthancPluginWorklistQueryOperation& p =
2737 *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
2738 *p.isMatch = reinterpret_cast<const WorklistHandler*>(p.query)->IsMatch(p.dicom, p.size);
2739 return true;
2740 }
2741
2742 case _OrthancPluginService_WorklistGetDicomQuery:
2743 {
2744 const _OrthancPluginWorklistQueryOperation& p =
2745 *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
2746 reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(*p.target);
2747 return true;
2748 }
2749
2750 case _OrthancPluginService_FindAddAnswer:
2751 {
2752 const _OrthancPluginFindOperation& p =
2753 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
2754 reinterpret_cast<DicomFindAnswers*>(p.answers)->Add(p.dicom, p.size);
2755 return true;
2756 }
2757
2758 case _OrthancPluginService_FindMarkIncomplete:
2759 {
2760 const _OrthancPluginFindOperation& p =
2761 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
2762 reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
2763 return true;
2764 }
2765
2766 case _OrthancPluginService_GetFindQuerySize:
2767 case _OrthancPluginService_GetFindQueryTag:
2768 case _OrthancPluginService_GetFindQueryTagName:
2769 case _OrthancPluginService_GetFindQueryValue:
2770 {
2771 const _OrthancPluginFindOperation& p =
2772 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
2773 reinterpret_cast<const FindHandler*>(p.query)->Invoke(service, p);
2774 return true;
2775 }
2776
2777 case _OrthancPluginService_CreateImage:
2778 case _OrthancPluginService_CreateImageAccessor:
2779 case _OrthancPluginService_DecodeDicomImage:
2780 ApplyCreateImage(service, parameters);
2781 return true;
2782
2783 case _OrthancPluginService_ComputeMd5:
2784 case _OrthancPluginService_ComputeSha1:
2785 ComputeHash(service, parameters);
2786 return true;
2787
2788 case _OrthancPluginService_LookupDictionary:
2789 ApplyLookupDictionary(parameters);
2790 return true;
2791
2792 case _OrthancPluginService_GenerateUuid:
2793 {
2794 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result =
2795 CopyString(Toolbox::GenerateUuid());
2796 return true;
2797 }
2798
2799 case _OrthancPluginService_CreateFindMatcher:
2800 {
2801 const _OrthancPluginCreateFindMatcher& p =
2802 *reinterpret_cast<const _OrthancPluginCreateFindMatcher*>(parameters);
2803 ParsedDicomFile query(p.query, p.size);
2804 *(p.target) = reinterpret_cast<OrthancPluginFindMatcher*>(new HierarchicalMatcher(query));
2805 return true;
2806 }
2807
2808 case _OrthancPluginService_FreeFindMatcher:
2809 {
2810 const _OrthancPluginFreeFindMatcher& p =
2811 *reinterpret_cast<const _OrthancPluginFreeFindMatcher*>(parameters);
2812
2813 if (p.matcher != NULL)
2814 {
2815 delete reinterpret_cast<HierarchicalMatcher*>(p.matcher);
2816 }
2817
2818 return true;
2819 }
2820
2821 case _OrthancPluginService_FindMatcherIsMatch:
2822 {
2823 const _OrthancPluginFindMatcherIsMatch& p =
2824 *reinterpret_cast<const _OrthancPluginFindMatcherIsMatch*>(parameters);
2825
2826 if (p.matcher == NULL)
2827 {
2828 throw OrthancException(ErrorCode_NullPointer);
2829 }
2830 else
2831 {
2832 ParsedDicomFile query(p.dicom, p.size);
2833 *p.isMatch = reinterpret_cast<const HierarchicalMatcher*>(p.matcher)->Match(query) ? 1 : 0;
2834 return true;
2835 }
2836 }
2837
2838 case _OrthancPluginService_GetPeers:
2839 {
2840 const _OrthancPluginGetPeers& p =
2841 *reinterpret_cast<const _OrthancPluginGetPeers*>(parameters);
2842 *(p.peers) = reinterpret_cast<OrthancPluginPeers*>(new OrthancPeers);
2843 return true;
2844 }
2845
2846 case _OrthancPluginService_FreePeers:
2847 {
2848 const _OrthancPluginFreePeers& p =
2849 *reinterpret_cast<const _OrthancPluginFreePeers*>(parameters);
2850
2851 if (p.peers != NULL)
2852 {
2853 delete reinterpret_cast<OrthancPeers*>(p.peers);
2854 }
2855
2856 return true;
2857 }
2858
2859 case _OrthancPluginService_GetPeersCount:
2860 {
2861 const _OrthancPluginGetPeersCount& p =
2862 *reinterpret_cast<const _OrthancPluginGetPeersCount*>(parameters);
2863
2864 if (p.peers == NULL)
2865 {
2866 throw OrthancException(ErrorCode_NullPointer);
2867 }
2868 else
2869 {
2870 *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeersCount();
2871 return true;
2872 }
2873 }
2874
2875 case _OrthancPluginService_GetPeerName:
2876 {
2877 const _OrthancPluginGetPeerProperty& p =
2878 *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters);
2879
2880 if (p.peers == NULL)
2881 {
2882 throw OrthancException(ErrorCode_NullPointer);
2883 }
2884 else
2885 {
2886 *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerName(p.peerIndex).c_str();
2887 return true;
2888 }
2889 }
2890
2891 case _OrthancPluginService_GetPeerUrl:
2892 {
2893 const _OrthancPluginGetPeerProperty& p =
2894 *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters);
2895
2896 if (p.peers == NULL)
2897 {
2898 throw OrthancException(ErrorCode_NullPointer);
2899 }
2900 else
2901 {
2902 *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerParameters(p.peerIndex).GetUrl().c_str();
2903 return true;
2904 }
2905 }
2906
2907 case _OrthancPluginService_GetPeerUserProperty:
2908 {
2909 const _OrthancPluginGetPeerProperty& p =
2910 *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters);
2911
2912 if (p.peers == NULL ||
2913 p.userProperty == NULL)
2914 {
2915 throw OrthancException(ErrorCode_NullPointer);
2916 }
2917 else
2918 {
2919 const WebServiceParameters::Dictionary& properties =
2920 reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerParameters(p.peerIndex).GetUserProperties();
2921
2922 WebServiceParameters::Dictionary::const_iterator found =
2923 properties.find(p.userProperty);
2924
2925 if (found == properties.end())
2926 {
2927 *(p.target) = NULL;
2928 }
2929 else
2930 {
2931 *(p.target) = found->second.c_str();
2932 }
2933
2934 return true;
2935 }
2936 }
2937
2938 case _OrthancPluginService_CallPeerApi:
2939 CallPeerApi(parameters);
2940 return true;
2941
2942 case _OrthancPluginService_CreateJob:
2943 {
2944 const _OrthancPluginCreateJob& p =
2945 *reinterpret_cast<const _OrthancPluginCreateJob*>(parameters);
2946 *(p.target) = reinterpret_cast<OrthancPluginJob*>(new PluginsJob(p));
2947 return true;
2948 }
2949
2950 case _OrthancPluginService_FreeJob:
2951 {
2952 const _OrthancPluginFreeJob& p =
2953 *reinterpret_cast<const _OrthancPluginFreeJob*>(parameters);
2954
2955 if (p.job != NULL)
2956 {
2957 delete reinterpret_cast<PluginsJob*>(p.job);
2958 }
2959
2960 return true;
2961 }
2962
2963 case _OrthancPluginService_SubmitJob:
2964 {
2965 const _OrthancPluginSubmitJob& p =
2966 *reinterpret_cast<const _OrthancPluginSubmitJob*>(parameters);
2967
2968 std::string uuid;
2969
2970 PImpl::ServerContextLock lock(*pimpl_);
2971 lock.GetContext().GetJobsEngine().GetRegistry().Submit
2972 (uuid, reinterpret_cast<PluginsJob*>(p.job), p.priority);
2973
2974 *p.resultId = CopyString(uuid);
2975
2976 return true;
2977 }
2978
2979 default:
2980 return false;
2981 }
2982 }
2983
2984
2985
2986 bool OrthancPlugins::InvokeProtectedService(SharedLibrary& plugin,
2987 _OrthancPluginService service,
2988 const void* parameters)
2989 {
2990 // Services that must be run in mutual exclusion. Guideline:
2991 // Whenever "pimpl_" is directly accessed by the service, it
2992 // should be listed here.
2993
2994 switch (service)
2995 {
2996 case _OrthancPluginService_RegisterRestCallback:
2997 RegisterRestCallback(parameters, true);
2998 return true;
2999
3000 case _OrthancPluginService_RegisterRestCallbackNoLock:
3001 RegisterRestCallback(parameters, false);
3002 return true;
3003
3004 case _OrthancPluginService_RegisterOnStoredInstanceCallback:
3005 RegisterOnStoredInstanceCallback(parameters);
3006 return true;
3007
3008 case _OrthancPluginService_RegisterOnChangeCallback:
3009 RegisterOnChangeCallback(parameters);
3010 return true;
3011
3012 case _OrthancPluginService_RegisterWorklistCallback:
3013 RegisterWorklistCallback(parameters);
3014 return true;
3015
3016 case _OrthancPluginService_RegisterFindCallback:
3017 RegisterFindCallback(parameters);
3018 return true;
3019
3020 case _OrthancPluginService_RegisterMoveCallback:
3021 RegisterMoveCallback(parameters);
3022 return true;
3023
3024 case _OrthancPluginService_RegisterDecodeImageCallback:
3025 RegisterDecodeImageCallback(parameters);
3026 return true;
3027
3028 case _OrthancPluginService_RegisterJobsUnserializer:
3029 RegisterJobsUnserializer(parameters);
3030 return true;
3031
3032 case _OrthancPluginService_RegisterIncomingHttpRequestFilter:
3033 RegisterIncomingHttpRequestFilter(parameters);
3034 return true;
3035
3036 case _OrthancPluginService_RegisterIncomingHttpRequestFilter2:
3037 RegisterIncomingHttpRequestFilter2(parameters);
3038 return true;
3039
3040 case _OrthancPluginService_RegisterStorageArea:
3041 {
3042 LOG(INFO) << "Plugin has registered a custom storage area";
3043 const _OrthancPluginRegisterStorageArea& p =
3044 *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
3045
3046 if (pimpl_->storageArea_.get() == NULL)
3047 {
3048 pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary()));
3049 }
3050 else
3051 {
3052 throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered);
3053 }
3054
3055 return true;
3056 }
3057
3058 case _OrthancPluginService_SetPluginProperty:
3059 {
3060 const _OrthancPluginSetPluginProperty& p =
3061 *reinterpret_cast<const _OrthancPluginSetPluginProperty*>(parameters);
3062 pimpl_->properties_[std::make_pair(p.plugin, p.property)] = p.value;
3063 return true;
3064 }
3065
3066 case _OrthancPluginService_GetCommandLineArgumentsCount:
3067 {
3068 const _OrthancPluginReturnSingleValue& p =
3069 *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
3070 *(p.resultUint32) = pimpl_->argc_ - 1;
3071 return true;
3072 }
3073
3074 case _OrthancPluginService_GetCommandLineArgument:
3075 {
3076 const _OrthancPluginGlobalProperty& p =
3077 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
3078
3079 if (p.property + 1 > pimpl_->argc_)
3080 {
3081 return false;
3082 }
3083 else
3084 {
3085 std::string arg = std::string(pimpl_->argv_[p.property + 1]);
3086 *(p.result) = CopyString(arg);
3087 return true;
3088 }
3089 }
3090
3091 case _OrthancPluginService_RegisterDatabaseBackend:
3092 {
3093 LOG(INFO) << "Plugin has registered a custom database back-end";
3094
3095 const _OrthancPluginRegisterDatabaseBackend& p =
3096 *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
3097
3098 if (pimpl_->database_.get() == NULL)
3099 {
3100 pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
3101 *p.backend, NULL, 0, p.payload));
3102 }
3103 else
3104 {
3105 throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
3106 }
3107
3108 *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
3109
3110 return true;
3111 }
3112
3113 case _OrthancPluginService_RegisterDatabaseBackendV2:
3114 {
3115 LOG(INFO) << "Plugin has registered a custom database back-end";
3116
3117 const _OrthancPluginRegisterDatabaseBackendV2& p =
3118 *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters);
3119
3120 if (pimpl_->database_.get() == NULL)
3121 {
3122 pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
3123 *p.backend, p.extensions,
3124 p.extensionsSize, p.payload));
3125 }
3126 else
3127 {
3128 throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
3129 }
3130
3131 *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
3132
3133 return true;
3134 }
3135
3136 case _OrthancPluginService_DatabaseAnswer:
3137 throw OrthancException(ErrorCode_InternalError); // Implemented before locking (*)
3138
1744 case _OrthancPluginService_RegisterErrorCode: 3139 case _OrthancPluginService_RegisterErrorCode:
1745 { 3140 {
1746 const _OrthancPluginRegisterErrorCode& p = 3141 const _OrthancPluginRegisterErrorCode& p =
1747 *reinterpret_cast<const _OrthancPluginRegisterErrorCode*>(parameters); 3142 *reinterpret_cast<const _OrthancPluginRegisterErrorCode*>(parameters);
1748 *(p.target) = pimpl_->dictionary_.Register(plugin, p.code, p.httpStatus, p.message); 3143 *(p.target) = pimpl_->dictionary_.Register(plugin, p.code, p.httpStatus, p.message);
1753 { 3148 {
1754 const _OrthancPluginRegisterDictionaryTag& p = 3149 const _OrthancPluginRegisterDictionaryTag& p =
1755 *reinterpret_cast<const _OrthancPluginRegisterDictionaryTag*>(parameters); 3150 *reinterpret_cast<const _OrthancPluginRegisterDictionaryTag*>(parameters);
1756 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element), 3151 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element),
1757 Plugins::Convert(p.vr), p.name, 3152 Plugins::Convert(p.vr), p.name,
1758 p.minMultiplicity, p.maxMultiplicity); 3153 p.minMultiplicity, p.maxMultiplicity, "");
3154 return true;
3155 }
3156
3157 case _OrthancPluginService_RegisterPrivateDictionaryTag:
3158 {
3159 const _OrthancPluginRegisterPrivateDictionaryTag& p =
3160 *reinterpret_cast<const _OrthancPluginRegisterPrivateDictionaryTag*>(parameters);
3161 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element),
3162 Plugins::Convert(p.vr), p.name,
3163 p.minMultiplicity, p.maxMultiplicity, p.privateCreator);
1759 return true; 3164 return true;
1760 } 3165 }
1761 3166
1762 case _OrthancPluginService_ReconstructMainDicomTags: 3167 case _OrthancPluginService_ReconstructMainDicomTags:
1763 { 3168 {
1769 LOG(ERROR) << "The service ReconstructMainDicomTags can only be invoked by custom database plugins"; 3174 LOG(ERROR) << "The service ReconstructMainDicomTags can only be invoked by custom database plugins";
1770 throw OrthancException(ErrorCode_DatabasePlugin); 3175 throw OrthancException(ErrorCode_DatabasePlugin);
1771 } 3176 }
1772 3177
1773 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea); 3178 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
1774 Toolbox::ReconstructMainDicomTags(*pimpl_->database_, storage, Plugins::Convert(p.level)); 3179 ServerToolbox::ReconstructMainDicomTags(*pimpl_->database_, storage, Plugins::Convert(p.level));
1775 3180
1776 return true; 3181 return true;
1777 } 3182 }
1778
1779 case _OrthancPluginService_DicomBufferToJson:
1780 case _OrthancPluginService_DicomInstanceToJson:
1781 ApplyDicomToJson(service, parameters);
1782 return true;
1783 3183
1784 default: 3184 default:
1785 { 3185 {
1786 // This service is unknown to the Orthanc plugin engine 3186 // This service is unknown to the Orthanc plugin engine
1787 return false; 3187 return false;
1788 } 3188 }
1789 } 3189 }
1790 } 3190 }
1791 3191
1792 3192
3193
3194 bool OrthancPlugins::InvokeService(SharedLibrary& plugin,
3195 _OrthancPluginService service,
3196 const void* parameters)
3197 {
3198 VLOG(1) << "Calling service " << service << " from plugin " << plugin.GetPath();
3199
3200 if (service == _OrthancPluginService_DatabaseAnswer)
3201 {
3202 // This case solves a deadlock at (*) reported by James Webster
3203 // on 2015-10-27 that was present in versions of Orthanc <=
3204 // 0.9.4 and related to database plugins implementing a custom
3205 // index. The problem was that locking the database is already
3206 // ensured by the "ServerIndex" class if the invoked service is
3207 // "DatabaseAnswer".
3208 DatabaseAnswer(parameters);
3209 return true;
3210 }
3211
3212 if (InvokeSafeService(plugin, service, parameters))
3213 {
3214 // The invoked service does not require locking
3215 return true;
3216 }
3217 else
3218 {
3219 // The invoked service requires locking
3220 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); // (*)
3221 return InvokeProtectedService(plugin, service, parameters);
3222 }
3223 }
3224
3225
1793 bool OrthancPlugins::HasStorageArea() const 3226 bool OrthancPlugins::HasStorageArea() const
1794 { 3227 {
3228 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
1795 return pimpl_->storageArea_.get() != NULL; 3229 return pimpl_->storageArea_.get() != NULL;
1796 } 3230 }
1797 3231
1798 bool OrthancPlugins::HasDatabaseBackend() const 3232 bool OrthancPlugins::HasDatabaseBackend() const
1799 { 3233 {
3234 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
1800 return pimpl_->database_.get() != NULL; 3235 return pimpl_->database_.get() != NULL;
1801 } 3236 }
1802 3237
1803 3238
1804 IStorageArea* OrthancPlugins::CreateStorageArea() 3239 IStorageArea* OrthancPlugins::CreateStorageArea()
1896 3331
1897 PluginsErrorDictionary& OrthancPlugins::GetErrorDictionary() 3332 PluginsErrorDictionary& OrthancPlugins::GetErrorDictionary()
1898 { 3333 {
1899 return pimpl_->dictionary_; 3334 return pimpl_->dictionary_;
1900 } 3335 }
3336
3337
3338 IWorklistRequestHandler* OrthancPlugins::ConstructWorklistRequestHandler()
3339 {
3340 if (HasWorklistHandler())
3341 {
3342 return new WorklistHandler(*this);
3343 }
3344 else
3345 {
3346 return NULL;
3347 }
3348 }
3349
3350
3351 bool OrthancPlugins::HasWorklistHandler()
3352 {
3353 boost::mutex::scoped_lock lock(pimpl_->worklistCallbackMutex_);
3354 return pimpl_->worklistCallback_ != NULL;
3355 }
3356
3357
3358 IFindRequestHandler* OrthancPlugins::ConstructFindRequestHandler()
3359 {
3360 if (HasFindHandler())
3361 {
3362 return new FindHandler(*this);
3363 }
3364 else
3365 {
3366 return NULL;
3367 }
3368 }
3369
3370
3371 bool OrthancPlugins::HasFindHandler()
3372 {
3373 boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_);
3374 return pimpl_->findCallback_ != NULL;
3375 }
3376
3377
3378 IMoveRequestHandler* OrthancPlugins::ConstructMoveRequestHandler()
3379 {
3380 if (HasMoveHandler())
3381 {
3382 return new MoveHandler(*this);
3383 }
3384 else
3385 {
3386 return NULL;
3387 }
3388 }
3389
3390
3391 bool OrthancPlugins::HasMoveHandler()
3392 {
3393 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
3394 return pimpl_->moveCallbacks_.callback != NULL;
3395 }
3396
3397
3398 bool OrthancPlugins::HasCustomImageDecoder()
3399 {
3400 boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
3401 return !pimpl_->decodeImageCallbacks_.empty();
3402 }
3403
3404
3405 ImageAccessor* OrthancPlugins::DecodeUnsafe(const void* dicom,
3406 size_t size,
3407 unsigned int frame)
3408 {
3409 boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);
3410
3411 for (PImpl::DecodeImageCallbacks::const_iterator
3412 decoder = pimpl_->decodeImageCallbacks_.begin();
3413 decoder != pimpl_->decodeImageCallbacks_.end(); ++decoder)
3414 {
3415 OrthancPluginImage* pluginImage = NULL;
3416 if ((*decoder) (&pluginImage, dicom, size, frame) == OrthancPluginErrorCode_Success &&
3417 pluginImage != NULL)
3418 {
3419 return reinterpret_cast<ImageAccessor*>(pluginImage);
3420 }
3421 }
3422
3423 return NULL;
3424 }
3425
3426
3427 ImageAccessor* OrthancPlugins::Decode(const void* dicom,
3428 size_t size,
3429 unsigned int frame)
3430 {
3431 ImageAccessor* result = DecodeUnsafe(dicom, size, frame);
3432
3433 if (result != NULL)
3434 {
3435 return result;
3436 }
3437 else
3438 {
3439 LOG(INFO) << "The installed image decoding plugins cannot handle an image, fallback to the built-in decoder";
3440 DefaultDicomImageDecoder defaultDecoder;
3441 return defaultDecoder.Decode(dicom, size, frame);
3442 }
3443 }
3444
3445
3446 bool OrthancPlugins::IsAllowed(HttpMethod method,
3447 const char* uri,
3448 const char* ip,
3449 const char* username,
3450 const IHttpHandler::Arguments& httpHeaders,
3451 const IHttpHandler::GetArguments& getArguments)
3452 {
3453 OrthancPluginHttpMethod cMethod = Plugins::Convert(method);
3454
3455 std::vector<const char*> httpKeys(httpHeaders.size());
3456 std::vector<const char*> httpValues(httpHeaders.size());
3457
3458 size_t pos = 0;
3459 for (IHttpHandler::Arguments::const_iterator
3460 it = httpHeaders.begin(); it != httpHeaders.end(); ++it, pos++)
3461 {
3462 httpKeys[pos] = it->first.c_str();
3463 httpValues[pos] = it->second.c_str();
3464 }
3465
3466 std::vector<const char*> getKeys(getArguments.size());
3467 std::vector<const char*> getValues(getArguments.size());
3468
3469 for (size_t i = 0; i < getArguments.size(); i++)
3470 {
3471 getKeys[i] = getArguments[i].first.c_str();
3472 getValues[i] = getArguments[i].second.c_str();
3473 }
3474
3475 // Improved callback with support for GET arguments, since Orthanc 1.3.0
3476 for (PImpl::IncomingHttpRequestFilters2::const_iterator
3477 filter = pimpl_->incomingHttpRequestFilters2_.begin();
3478 filter != pimpl_->incomingHttpRequestFilters2_.end(); ++filter)
3479 {
3480 int32_t allowed = (*filter) (cMethod, uri, ip,
3481 httpKeys.size(),
3482 httpKeys.empty() ? NULL : &httpKeys[0],
3483 httpValues.empty() ? NULL : &httpValues[0],
3484 getKeys.size(),
3485 getKeys.empty() ? NULL : &getKeys[0],
3486 getValues.empty() ? NULL : &getValues[0]);
3487
3488 if (allowed == 0)
3489 {
3490 return false;
3491 }
3492 else if (allowed != 1)
3493 {
3494 // The callback is only allowed to answer 0 or 1
3495 throw OrthancException(ErrorCode_Plugin);
3496 }
3497 }
3498
3499 for (PImpl::IncomingHttpRequestFilters::const_iterator
3500 filter = pimpl_->incomingHttpRequestFilters_.begin();
3501 filter != pimpl_->incomingHttpRequestFilters_.end(); ++filter)
3502 {
3503 int32_t allowed = (*filter) (cMethod, uri, ip, httpKeys.size(),
3504 httpKeys.empty() ? NULL : &httpKeys[0],
3505 httpValues.empty() ? NULL : &httpValues[0]);
3506
3507 if (allowed == 0)
3508 {
3509 return false;
3510 }
3511 else if (allowed != 1)
3512 {
3513 // The callback is only allowed to answer 0 or 1
3514 throw OrthancException(ErrorCode_Plugin);
3515 }
3516 }
3517
3518 return true;
3519 }
3520
3521
3522 IJob* OrthancPlugins::UnserializeJob(const std::string& type,
3523 const Json::Value& value)
3524 {
3525 const std::string serialized = value.toStyledString();
3526
3527 boost::mutex::scoped_lock lock(pimpl_->jobsUnserializersMutex_);
3528
3529 for (PImpl::JobsUnserializers::iterator
3530 unserializer = pimpl_->jobsUnserializers_.begin();
3531 unserializer != pimpl_->jobsUnserializers_.end(); ++unserializer)
3532 {
3533 OrthancPluginJob* job = (*unserializer) (type.c_str(), serialized.c_str());
3534 if (job != NULL)
3535 {
3536 return reinterpret_cast<PluginsJob*>(job);
3537 }
3538 }
3539
3540 return NULL;
3541 }
1901 } 3542 }