comparison OrthancServer/Plugins/Engine/OrthancPlugins.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents Plugins/Engine/OrthancPlugins.cpp@e42f5445d20d
children 05b8fd21089c
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34 #include "../../OrthancServer/PrecompiledHeadersServer.h"
35 #include "OrthancPlugins.h"
36
37 #if ORTHANC_ENABLE_PLUGINS != 1
38 #error The plugin support is disabled
39 #endif
40
41 #if !defined(DCMTK_VERSION_NUMBER)
42 # error The macro DCMTK_VERSION_NUMBER must be defined
43 #endif
44
45
46 #include "../../Core/Compression/GzipCompressor.h"
47 #include "../../Core/Compression/ZlibCompressor.h"
48 #include "../../Core/DicomFormat/DicomArray.h"
49 #include "../../Core/DicomParsing/DicomWebJsonVisitor.h"
50 #include "../../Core/DicomParsing/FromDcmtkBridge.h"
51 #include "../../Core/DicomParsing/Internals/DicomImageDecoder.h"
52 #include "../../Core/DicomParsing/ToDcmtkBridge.h"
53 #include "../../Core/HttpServer/HttpToolbox.h"
54 #include "../../Core/Images/Image.h"
55 #include "../../Core/Images/ImageProcessing.h"
56 #include "../../Core/Images/JpegReader.h"
57 #include "../../Core/Images/JpegWriter.h"
58 #include "../../Core/Images/PngReader.h"
59 #include "../../Core/Images/PngWriter.h"
60 #include "../../Core/Logging.h"
61 #include "../../Core/MetricsRegistry.h"
62 #include "../../Core/OrthancException.h"
63 #include "../../Core/SerializationToolbox.h"
64 #include "../../Core/Toolbox.h"
65 #include "../../OrthancServer/OrthancConfiguration.h"
66 #include "../../OrthancServer/OrthancFindRequestHandler.h"
67 #include "../../OrthancServer/Search/HierarchicalMatcher.h"
68 #include "../../OrthancServer/ServerContext.h"
69 #include "../../OrthancServer/ServerToolbox.h"
70 #include "PluginsEnumerations.h"
71 #include "PluginsJob.h"
72
73 #include <boost/regex.hpp>
74 #include <dcmtk/dcmdata/dcdict.h>
75 #include <dcmtk/dcmdata/dcdicent.h>
76
77 #define ERROR_MESSAGE_64BIT "A 64bit version of the Orthanc API is necessary"
78
79
80 namespace Orthanc
81 {
82 static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
83 const void* data,
84 size_t size)
85 {
86 if (static_cast<uint32_t>(size) != size)
87 {
88 throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
89 }
90
91 target.size = size;
92
93 if (size == 0)
94 {
95 target.data = NULL;
96 }
97 else
98 {
99 target.data = malloc(size);
100 if (target.data != NULL)
101 {
102 memcpy(target.data, data, size);
103 }
104 else
105 {
106 throw OrthancException(ErrorCode_NotEnoughMemory);
107 }
108 }
109 }
110
111
112 static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
113 const std::string& str)
114 {
115 if (str.size() == 0)
116 {
117 target.size = 0;
118 target.data = NULL;
119 }
120 else
121 {
122 CopyToMemoryBuffer(target, str.c_str(), str.size());
123 }
124 }
125
126
127 static char* CopyString(const std::string& str)
128 {
129 if (static_cast<uint32_t>(str.size()) != str.size())
130 {
131 throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
132 }
133
134 char *result = reinterpret_cast<char*>(malloc(str.size() + 1));
135 if (result == NULL)
136 {
137 throw OrthancException(ErrorCode_NotEnoughMemory);
138 }
139
140 if (str.size() == 0)
141 {
142 result[0] = '\0';
143 }
144 else
145 {
146 memcpy(result, &str[0], str.size() + 1);
147 }
148
149 return result;
150 }
151
152
153 namespace
154 {
155 class PluginStorageArea : public IStorageArea
156 {
157 private:
158 _OrthancPluginRegisterStorageArea callbacks_;
159 PluginsErrorDictionary& errorDictionary_;
160
161 void Free(void* buffer) const
162 {
163 if (buffer != NULL)
164 {
165 callbacks_.free(buffer);
166 }
167 }
168
169 public:
170 PluginStorageArea(const _OrthancPluginRegisterStorageArea& callbacks,
171 PluginsErrorDictionary& errorDictionary) :
172 callbacks_(callbacks),
173 errorDictionary_(errorDictionary)
174 {
175 }
176
177
178 virtual void Create(const std::string& uuid,
179 const void* content,
180 size_t size,
181 FileContentType type)
182 {
183 OrthancPluginErrorCode error = callbacks_.create
184 (uuid.c_str(), content, size, Plugins::Convert(type));
185
186 if (error != OrthancPluginErrorCode_Success)
187 {
188 errorDictionary_.LogError(error, true);
189 throw OrthancException(static_cast<ErrorCode>(error));
190 }
191 }
192
193
194 virtual void Read(std::string& content,
195 const std::string& uuid,
196 FileContentType type)
197 {
198 void* buffer = NULL;
199 int64_t size = 0;
200
201 OrthancPluginErrorCode error = callbacks_.read
202 (&buffer, &size, uuid.c_str(), Plugins::Convert(type));
203
204 if (error != OrthancPluginErrorCode_Success)
205 {
206 errorDictionary_.LogError(error, true);
207 throw OrthancException(static_cast<ErrorCode>(error));
208 }
209
210 try
211 {
212 content.resize(static_cast<size_t>(size));
213 }
214 catch (...)
215 {
216 Free(buffer);
217 throw OrthancException(ErrorCode_NotEnoughMemory);
218 }
219
220 if (size > 0)
221 {
222 memcpy(&content[0], buffer, static_cast<size_t>(size));
223 }
224
225 Free(buffer);
226 }
227
228
229 virtual void Remove(const std::string& uuid,
230 FileContentType type)
231 {
232 OrthancPluginErrorCode error = callbacks_.remove
233 (uuid.c_str(), Plugins::Convert(type));
234
235 if (error != OrthancPluginErrorCode_Success)
236 {
237 errorDictionary_.LogError(error, true);
238 throw OrthancException(static_cast<ErrorCode>(error));
239 }
240 }
241 };
242
243
244 class StorageAreaFactory : public boost::noncopyable
245 {
246 private:
247 SharedLibrary& sharedLibrary_;
248 _OrthancPluginRegisterStorageArea callbacks_;
249 PluginsErrorDictionary& errorDictionary_;
250
251 public:
252 StorageAreaFactory(SharedLibrary& sharedLibrary,
253 const _OrthancPluginRegisterStorageArea& callbacks,
254 PluginsErrorDictionary& errorDictionary) :
255 sharedLibrary_(sharedLibrary),
256 callbacks_(callbacks),
257 errorDictionary_(errorDictionary)
258 {
259 }
260
261 SharedLibrary& GetSharedLibrary()
262 {
263 return sharedLibrary_;
264 }
265
266 IStorageArea* Create() const
267 {
268 return new PluginStorageArea(callbacks_, errorDictionary_);
269 }
270 };
271
272
273 class OrthancPeers : public boost::noncopyable
274 {
275 private:
276 std::vector<std::string> names_;
277 std::vector<WebServiceParameters> parameters_;
278
279 void CheckIndex(size_t i) const
280 {
281 assert(names_.size() == parameters_.size());
282 if (i >= names_.size())
283 {
284 throw OrthancException(ErrorCode_ParameterOutOfRange);
285 }
286 }
287
288 public:
289 OrthancPeers()
290 {
291 OrthancConfiguration::ReaderLock lock;
292
293 std::set<std::string> peers;
294 lock.GetConfiguration().GetListOfOrthancPeers(peers);
295
296 names_.reserve(peers.size());
297 parameters_.reserve(peers.size());
298
299 for (std::set<std::string>::const_iterator
300 it = peers.begin(); it != peers.end(); ++it)
301 {
302 WebServiceParameters peer;
303 if (lock.GetConfiguration().LookupOrthancPeer(peer, *it))
304 {
305 names_.push_back(*it);
306 parameters_.push_back(peer);
307 }
308 }
309 }
310
311 size_t GetPeersCount() const
312 {
313 return names_.size();
314 }
315
316 const std::string& GetPeerName(size_t i) const
317 {
318 CheckIndex(i);
319 return names_[i];
320 }
321
322 const WebServiceParameters& GetPeerParameters(size_t i) const
323 {
324 CheckIndex(i);
325 return parameters_[i];
326 }
327 };
328
329
330 class DicomWebBinaryFormatter : public DicomWebJsonVisitor::IBinaryFormatter
331 {
332 private:
333 OrthancPluginDicomWebBinaryCallback oldCallback_;
334 OrthancPluginDicomWebBinaryCallback2 newCallback_; // New in Orthanc 1.7.0
335 void* newPayload_; // New in Orthanc 1.7.0
336 DicomWebJsonVisitor::BinaryMode currentMode_;
337 std::string currentBulkDataUri_;
338
339 static void Setter(OrthancPluginDicomWebNode* node,
340 OrthancPluginDicomWebBinaryMode mode,
341 const char* bulkDataUri)
342 {
343 DicomWebBinaryFormatter& that = *reinterpret_cast<DicomWebBinaryFormatter*>(node);
344
345 switch (mode)
346 {
347 case OrthancPluginDicomWebBinaryMode_Ignore:
348 that.currentMode_ = DicomWebJsonVisitor::BinaryMode_Ignore;
349 break;
350
351 case OrthancPluginDicomWebBinaryMode_InlineBinary:
352 that.currentMode_ = DicomWebJsonVisitor::BinaryMode_InlineBinary;
353 break;
354
355 case OrthancPluginDicomWebBinaryMode_BulkDataUri:
356 if (bulkDataUri == NULL)
357 {
358 throw OrthancException(ErrorCode_NullPointer);
359 }
360
361 that.currentBulkDataUri_ = bulkDataUri;
362 that.currentMode_ = DicomWebJsonVisitor::BinaryMode_BulkDataUri;
363 break;
364
365 default:
366 throw OrthancException(ErrorCode_ParameterOutOfRange);
367 }
368 }
369
370 public:
371 DicomWebBinaryFormatter(OrthancPluginDicomWebBinaryCallback callback) :
372 oldCallback_(callback),
373 newCallback_(NULL),
374 newPayload_(NULL)
375 {
376 }
377
378 DicomWebBinaryFormatter(OrthancPluginDicomWebBinaryCallback2 callback,
379 void* payload) :
380 oldCallback_(NULL),
381 newCallback_(callback),
382 newPayload_(payload)
383 {
384 }
385
386 virtual DicomWebJsonVisitor::BinaryMode Format(std::string& bulkDataUri,
387 const std::vector<DicomTag>& parentTags,
388 const std::vector<size_t>& parentIndexes,
389 const DicomTag& tag,
390 ValueRepresentation vr)
391 {
392 if (oldCallback_ == NULL &&
393 newCallback_ == NULL)
394 {
395 return DicomWebJsonVisitor::BinaryMode_InlineBinary;
396 }
397 else
398 {
399 assert(parentTags.size() == parentIndexes.size());
400 std::vector<uint16_t> groups(parentTags.size());
401 std::vector<uint16_t> elements(parentTags.size());
402 std::vector<uint32_t> indexes(parentTags.size());
403
404 for (size_t i = 0; i < parentTags.size(); i++)
405 {
406 groups[i] = parentTags[i].GetGroup();
407 elements[i] = parentTags[i].GetElement();
408 indexes[i] = static_cast<uint32_t>(parentIndexes[i]);
409 }
410 bool empty = parentTags.empty();
411
412 currentMode_ = DicomWebJsonVisitor::BinaryMode_Ignore;
413
414 if (oldCallback_ != NULL)
415 {
416 oldCallback_(reinterpret_cast<OrthancPluginDicomWebNode*>(this),
417 DicomWebBinaryFormatter::Setter,
418 static_cast<uint32_t>(parentTags.size()),
419 (empty ? NULL : &groups[0]),
420 (empty ? NULL : &elements[0]),
421 (empty ? NULL : &indexes[0]),
422 tag.GetGroup(),
423 tag.GetElement(),
424 Plugins::Convert(vr));
425 }
426 else
427 {
428 assert(newCallback_ != NULL);
429 newCallback_(reinterpret_cast<OrthancPluginDicomWebNode*>(this),
430 DicomWebBinaryFormatter::Setter,
431 static_cast<uint32_t>(parentTags.size()),
432 (empty ? NULL : &groups[0]),
433 (empty ? NULL : &elements[0]),
434 (empty ? NULL : &indexes[0]),
435 tag.GetGroup(),
436 tag.GetElement(),
437 Plugins::Convert(vr),
438 newPayload_);
439 }
440
441 bulkDataUri = currentBulkDataUri_;
442 return currentMode_;
443 }
444 }
445
446 void Apply(char** target,
447 bool isJson,
448 ParsedDicomFile& dicom)
449 {
450 DicomWebJsonVisitor visitor;
451 visitor.SetFormatter(*this);
452
453 dicom.Apply(visitor);
454
455 std::string s;
456
457 if (isJson)
458 {
459 s = visitor.GetResult().toStyledString();
460 }
461 else
462 {
463 visitor.FormatXml(s);
464 }
465
466 *target = CopyString(s);
467 }
468
469
470 void Apply(char** target,
471 bool isJson,
472 const void* dicom,
473 size_t dicomSize)
474 {
475 ParsedDicomFile parsed(dicom, dicomSize);
476 Apply(target, isJson, parsed);
477 }
478 };
479 }
480
481
482 class OrthancPlugins::PImpl
483 {
484 private:
485 boost::mutex contextMutex_;
486 ServerContext* context_;
487
488 public:
489 class PluginHttpOutput : public boost::noncopyable
490 {
491 private:
492 enum MultipartState
493 {
494 MultipartState_None,
495 MultipartState_FirstPart,
496 MultipartState_SecondPart,
497 MultipartState_NextParts
498 };
499
500 HttpOutput& output_;
501 std::unique_ptr<std::string> errorDetails_;
502 bool logDetails_;
503 MultipartState multipartState_;
504 std::string multipartSubType_;
505 std::string multipartContentType_;
506 std::string multipartFirstPart_;
507 std::map<std::string, std::string> multipartFirstHeaders_;
508
509 public:
510 PluginHttpOutput(HttpOutput& output) :
511 output_(output),
512 logDetails_(false),
513 multipartState_(MultipartState_None)
514 {
515 }
516
517 HttpOutput& GetOutput()
518 {
519 if (multipartState_ == MultipartState_None)
520 {
521 return output_;
522 }
523 else
524 {
525 // Must use "SendMultipartItem()" on multipart streams
526 throw OrthancException(ErrorCode_BadSequenceOfCalls);
527 }
528 }
529
530 void SetErrorDetails(const std::string& details,
531 bool logDetails)
532 {
533 errorDetails_.reset(new std::string(details));
534 logDetails_ = logDetails;
535 }
536
537 bool HasErrorDetails() const
538 {
539 return errorDetails_.get() != NULL;
540 }
541
542 bool IsLogDetails() const
543 {
544 return logDetails_;
545 }
546
547 const std::string& GetErrorDetails() const
548 {
549 if (errorDetails_.get() == NULL)
550 {
551 throw OrthancException(ErrorCode_BadSequenceOfCalls);
552 }
553 else
554 {
555 return *errorDetails_;
556 }
557 }
558
559 void StartMultipart(const char* subType,
560 const char* contentType)
561 {
562 if (multipartState_ != MultipartState_None)
563 {
564 throw OrthancException(ErrorCode_BadSequenceOfCalls);
565 }
566 else
567 {
568 multipartState_ = MultipartState_FirstPart;
569 multipartSubType_ = subType;
570 multipartContentType_ = contentType;
571 }
572 }
573
574 void SendMultipartItem(const void* data,
575 size_t size,
576 const std::map<std::string, std::string>& headers)
577 {
578 if (size != 0 && data == NULL)
579 {
580 throw OrthancException(ErrorCode_NullPointer);
581 }
582
583 switch (multipartState_)
584 {
585 case MultipartState_None:
586 // Must call "StartMultipart()" before
587 throw OrthancException(ErrorCode_BadSequenceOfCalls);
588
589 case MultipartState_FirstPart:
590 multipartFirstPart_.assign(reinterpret_cast<const char*>(data), size);
591 multipartFirstHeaders_ = headers;
592 multipartState_ = MultipartState_SecondPart;
593 break;
594
595 case MultipartState_SecondPart:
596 // Start an actual stream for chunked transfer as soon as
597 // there are more than 2 elements in the multipart stream
598 output_.StartMultipart(multipartSubType_, multipartContentType_);
599 output_.SendMultipartItem(multipartFirstPart_.c_str(), multipartFirstPart_.size(),
600 multipartFirstHeaders_);
601 multipartFirstPart_.clear(); // Release memory
602
603 output_.SendMultipartItem(data, size, headers);
604 multipartState_ = MultipartState_NextParts;
605 break;
606
607 case MultipartState_NextParts:
608 output_.SendMultipartItem(data, size, headers);
609 break;
610
611 default:
612 throw OrthancException(ErrorCode_ParameterOutOfRange);
613 }
614 }
615
616 void Close(OrthancPluginErrorCode error,
617 PluginsErrorDictionary& dictionary)
618 {
619 if (error == OrthancPluginErrorCode_Success)
620 {
621 switch (multipartState_)
622 {
623 case MultipartState_None:
624 assert(!output_.IsWritingMultipart());
625 break;
626
627 case MultipartState_FirstPart: // Multipart started, but no part was sent
628 case MultipartState_SecondPart: // Multipart started, first part is pending
629 {
630 assert(!output_.IsWritingMultipart());
631 std::vector<const void*> parts;
632 std::vector<size_t> sizes;
633 std::vector<const std::map<std::string, std::string>*> headers;
634
635 if (multipartState_ == MultipartState_SecondPart)
636 {
637 parts.push_back(multipartFirstPart_.c_str());
638 sizes.push_back(multipartFirstPart_.size());
639 headers.push_back(&multipartFirstHeaders_);
640 }
641
642 output_.AnswerMultipartWithoutChunkedTransfer(multipartSubType_, multipartContentType_,
643 parts, sizes, headers);
644 break;
645 }
646
647 case MultipartState_NextParts:
648 assert(output_.IsWritingMultipart());
649 output_.CloseMultipart();
650
651 default:
652 throw OrthancException(ErrorCode_InternalError);
653 }
654 }
655 else
656 {
657 dictionary.LogError(error, false);
658
659 if (HasErrorDetails())
660 {
661 throw OrthancException(static_cast<ErrorCode>(error),
662 GetErrorDetails(),
663 IsLogDetails());
664 }
665 else
666 {
667 throw OrthancException(static_cast<ErrorCode>(error));
668 }
669 }
670 }
671 };
672
673
674 class RestCallback : public boost::noncopyable
675 {
676 private:
677 boost::regex regex_;
678 OrthancPluginRestCallback callback_;
679 bool lock_;
680
681 OrthancPluginErrorCode InvokeInternal(PluginHttpOutput& output,
682 const std::string& flatUri,
683 const OrthancPluginHttpRequest& request)
684 {
685 return callback_(reinterpret_cast<OrthancPluginRestOutput*>(&output),
686 flatUri.c_str(),
687 &request);
688 }
689
690 public:
691 RestCallback(const char* regex,
692 OrthancPluginRestCallback callback,
693 bool lockRestCallbacks) :
694 regex_(regex),
695 callback_(callback),
696 lock_(lockRestCallbacks)
697 {
698 }
699
700 const boost::regex& GetRegularExpression() const
701 {
702 return regex_;
703 }
704
705 OrthancPluginErrorCode Invoke(boost::recursive_mutex& restCallbackMutex,
706 PluginHttpOutput& output,
707 const std::string& flatUri,
708 const OrthancPluginHttpRequest& request)
709 {
710 if (lock_)
711 {
712 boost::recursive_mutex::scoped_lock lock(restCallbackMutex);
713 return InvokeInternal(output, flatUri, request);
714 }
715 else
716 {
717 return InvokeInternal(output, flatUri, request);
718 }
719 }
720 };
721
722
723 class ChunkedRestCallback : public boost::noncopyable
724 {
725 private:
726 _OrthancPluginChunkedRestCallback parameters_;
727 boost::regex regex_;
728
729 public:
730 ChunkedRestCallback(_OrthancPluginChunkedRestCallback parameters) :
731 parameters_(parameters),
732 regex_(parameters.pathRegularExpression)
733 {
734 }
735
736 const boost::regex& GetRegularExpression() const
737 {
738 return regex_;
739 }
740
741 const _OrthancPluginChunkedRestCallback& GetParameters() const
742 {
743 return parameters_;
744 }
745 };
746
747
748
749 class StorageCommitmentScp : public IStorageCommitmentFactory
750 {
751 private:
752 class Handler : public IStorageCommitmentFactory::ILookupHandler
753 {
754 private:
755 _OrthancPluginRegisterStorageCommitmentScpCallback parameters_;
756 void* handler_;
757
758 public:
759 Handler(_OrthancPluginRegisterStorageCommitmentScpCallback parameters,
760 void* handler) :
761 parameters_(parameters),
762 handler_(handler)
763 {
764 if (handler == NULL)
765 {
766 throw OrthancException(ErrorCode_NullPointer);
767 }
768 }
769
770 virtual ~Handler()
771 {
772 assert(handler_ != NULL);
773 parameters_.destructor(handler_);
774 handler_ = NULL;
775 }
776
777 virtual StorageCommitmentFailureReason Lookup(const std::string& sopClassUid,
778 const std::string& sopInstanceUid)
779 {
780 assert(handler_ != NULL);
781 OrthancPluginStorageCommitmentFailureReason reason =
782 OrthancPluginStorageCommitmentFailureReason_Success;
783 OrthancPluginErrorCode error = parameters_.lookup(
784 &reason, handler_, sopClassUid.c_str(), sopInstanceUid.c_str());
785 if (error == OrthancPluginErrorCode_Success)
786 {
787 return Plugins::Convert(reason);
788 }
789 else
790 {
791 throw OrthancException(static_cast<ErrorCode>(error));
792 }
793 }
794 };
795
796 _OrthancPluginRegisterStorageCommitmentScpCallback parameters_;
797
798 public:
799 StorageCommitmentScp(_OrthancPluginRegisterStorageCommitmentScpCallback parameters) :
800 parameters_(parameters)
801 {
802 }
803
804 virtual ILookupHandler* CreateStorageCommitment(
805 const std::string& jobId,
806 const std::string& transactionUid,
807 const std::vector<std::string>& sopClassUids,
808 const std::vector<std::string>& sopInstanceUids,
809 const std::string& remoteAet,
810 const std::string& calledAet) ORTHANC_OVERRIDE
811 {
812 const size_t n = sopClassUids.size();
813
814 if (sopInstanceUids.size() != n)
815 {
816 throw OrthancException(ErrorCode_ParameterOutOfRange);
817 }
818
819 std::vector<const char*> a, b;
820 a.resize(n);
821 b.resize(n);
822
823 for (size_t i = 0; i < n; i++)
824 {
825 a[i] = sopClassUids[i].c_str();
826 b[i] = sopInstanceUids[i].c_str();
827 }
828
829 void* handler = NULL;
830 OrthancPluginErrorCode error = parameters_.factory(
831 &handler, jobId.c_str(), transactionUid.c_str(),
832 a.empty() ? NULL : &a[0], b.empty() ? NULL : &b[0], static_cast<uint32_t>(n),
833 remoteAet.c_str(), calledAet.c_str());
834
835 if (error != OrthancPluginErrorCode_Success)
836 {
837 throw OrthancException(static_cast<ErrorCode>(error));
838 }
839 else if (handler == NULL)
840 {
841 // This plugin won't handle this storage commitment request
842 return NULL;
843 }
844 else
845 {
846 return new Handler(parameters_, handler);
847 }
848 }
849 };
850
851
852 class ServerContextLock
853 {
854 private:
855 boost::mutex::scoped_lock lock_;
856 ServerContext* context_;
857
858 public:
859 ServerContextLock(PImpl& that) :
860 lock_(that.contextMutex_),
861 context_(that.context_)
862 {
863 if (context_ == NULL)
864 {
865 throw OrthancException(ErrorCode_DatabaseNotInitialized);
866 }
867 }
868
869 ServerContext& GetContext()
870 {
871 assert(context_ != NULL);
872 return *context_;
873 }
874 };
875
876
877 void SetServerContext(ServerContext* context)
878 {
879 boost::mutex::scoped_lock lock(contextMutex_);
880 context_ = context;
881 }
882
883
884 typedef std::pair<std::string, _OrthancPluginProperty> Property;
885 typedef std::list<RestCallback*> RestCallbacks;
886 typedef std::list<ChunkedRestCallback*> ChunkedRestCallbacks;
887 typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks;
888 typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks;
889 typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters;
890 typedef std::list<OrthancPluginIncomingHttpRequestFilter2> IncomingHttpRequestFilters2;
891 typedef std::list<OrthancPluginIncomingDicomInstanceFilter> IncomingDicomInstanceFilters;
892 typedef std::list<OrthancPluginDecodeImageCallback> DecodeImageCallbacks;
893 typedef std::list<OrthancPluginTranscoderCallback> TranscoderCallbacks;
894 typedef std::list<OrthancPluginJobsUnserializer> JobsUnserializers;
895 typedef std::list<OrthancPluginRefreshMetricsCallback> RefreshMetricsCallbacks;
896 typedef std::list<StorageCommitmentScp*> StorageCommitmentScpCallbacks;
897 typedef std::map<Property, std::string> Properties;
898
899 PluginsManager manager_;
900
901 RestCallbacks restCallbacks_;
902 ChunkedRestCallbacks chunkedRestCallbacks_;
903 OnStoredCallbacks onStoredCallbacks_;
904 OnChangeCallbacks onChangeCallbacks_;
905 OrthancPluginFindCallback findCallback_;
906 OrthancPluginWorklistCallback worklistCallback_;
907 DecodeImageCallbacks decodeImageCallbacks_;
908 TranscoderCallbacks transcoderCallbacks_;
909 JobsUnserializers jobsUnserializers_;
910 _OrthancPluginMoveCallback moveCallbacks_;
911 IncomingHttpRequestFilters incomingHttpRequestFilters_;
912 IncomingHttpRequestFilters2 incomingHttpRequestFilters2_;
913 IncomingDicomInstanceFilters incomingDicomInstanceFilters_;
914 RefreshMetricsCallbacks refreshMetricsCallbacks_;
915 StorageCommitmentScpCallbacks storageCommitmentScpCallbacks_;
916 std::unique_ptr<StorageAreaFactory> storageArea_;
917
918 boost::recursive_mutex restCallbackMutex_;
919 boost::recursive_mutex storedCallbackMutex_;
920 boost::recursive_mutex changeCallbackMutex_;
921 boost::mutex findCallbackMutex_;
922 boost::mutex worklistCallbackMutex_;
923 boost::shared_mutex decoderTranscoderMutex_; // Changed from "boost::mutex" in Orthanc 1.7.0
924 boost::mutex jobsUnserializersMutex_;
925 boost::mutex refreshMetricsMutex_;
926 boost::mutex storageCommitmentScpMutex_;
927 boost::recursive_mutex invokeServiceMutex_;
928
929 Properties properties_;
930 int argc_;
931 char** argv_;
932 std::unique_ptr<OrthancPluginDatabase> database_;
933 PluginsErrorDictionary dictionary_;
934
935 PImpl() :
936 context_(NULL),
937 findCallback_(NULL),
938 worklistCallback_(NULL),
939 argc_(1),
940 argv_(NULL)
941 {
942 memset(&moveCallbacks_, 0, sizeof(moveCallbacks_));
943 }
944 };
945
946
947
948 class OrthancPlugins::WorklistHandler : public IWorklistRequestHandler
949 {
950 private:
951 OrthancPlugins& that_;
952 std::unique_ptr<HierarchicalMatcher> matcher_;
953 std::unique_ptr<ParsedDicomFile> filtered_;
954 ParsedDicomFile* currentQuery_;
955
956 void Reset()
957 {
958 matcher_.reset();
959 filtered_.reset();
960 currentQuery_ = NULL;
961 }
962
963 public:
964 WorklistHandler(OrthancPlugins& that) : that_(that)
965 {
966 Reset();
967 }
968
969 virtual void Handle(DicomFindAnswers& answers,
970 ParsedDicomFile& query,
971 const std::string& remoteIp,
972 const std::string& remoteAet,
973 const std::string& calledAet,
974 ModalityManufacturer manufacturer)
975 {
976 static const char* LUA_CALLBACK = "IncomingWorklistRequestFilter";
977
978 {
979 PImpl::ServerContextLock lock(*that_.pimpl_);
980 LuaScripting::Lock lua(lock.GetContext().GetLuaScripting());
981
982 if (!lua.GetLua().IsExistingFunction(LUA_CALLBACK))
983 {
984 currentQuery_ = &query;
985 }
986 else
987 {
988 Json::Value source, origin;
989 query.DatasetToJson(source, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
990
991 OrthancFindRequestHandler::FormatOrigin
992 (origin, remoteIp, remoteAet, calledAet, manufacturer);
993
994 LuaFunctionCall call(lua.GetLua(), LUA_CALLBACK);
995 call.PushJson(source);
996 call.PushJson(origin);
997
998 Json::Value target;
999 call.ExecuteToJson(target, true);
1000
1001 filtered_.reset(ParsedDicomFile::CreateFromJson(target, DicomFromJsonFlags_None,
1002 "" /* no private creator */));
1003 currentQuery_ = filtered_.get();
1004 }
1005 }
1006
1007 matcher_.reset(new HierarchicalMatcher(*currentQuery_));
1008
1009 {
1010 boost::mutex::scoped_lock lock(that_.pimpl_->worklistCallbackMutex_);
1011
1012 if (that_.pimpl_->worklistCallback_)
1013 {
1014 OrthancPluginErrorCode error = that_.pimpl_->worklistCallback_
1015 (reinterpret_cast<OrthancPluginWorklistAnswers*>(&answers),
1016 reinterpret_cast<const OrthancPluginWorklistQuery*>(this),
1017 remoteAet.c_str(),
1018 calledAet.c_str());
1019
1020 if (error != OrthancPluginErrorCode_Success)
1021 {
1022 Reset();
1023 that_.GetErrorDictionary().LogError(error, true);
1024 throw OrthancException(static_cast<ErrorCode>(error));
1025 }
1026 }
1027
1028 Reset();
1029 }
1030 }
1031
1032 void GetDicomQuery(OrthancPluginMemoryBuffer& target) const
1033 {
1034 if (currentQuery_ == NULL)
1035 {
1036 throw OrthancException(ErrorCode_Plugin);
1037 }
1038
1039 std::string dicom;
1040 currentQuery_->SaveToMemoryBuffer(dicom);
1041 CopyToMemoryBuffer(target, dicom.c_str(), dicom.size());
1042 }
1043
1044 bool IsMatch(const void* dicom,
1045 size_t size) const
1046 {
1047 if (matcher_.get() == NULL)
1048 {
1049 throw OrthancException(ErrorCode_Plugin);
1050 }
1051
1052 ParsedDicomFile f(dicom, size);
1053 return matcher_->Match(f);
1054 }
1055
1056 void AddAnswer(OrthancPluginWorklistAnswers* answers,
1057 const void* dicom,
1058 size_t size) const
1059 {
1060 if (matcher_.get() == NULL)
1061 {
1062 throw OrthancException(ErrorCode_Plugin);
1063 }
1064
1065 ParsedDicomFile f(dicom, size);
1066 std::unique_ptr<ParsedDicomFile> summary(matcher_->Extract(f));
1067 reinterpret_cast<DicomFindAnswers*>(answers)->Add(*summary);
1068 }
1069 };
1070
1071
1072 class OrthancPlugins::FindHandler : public IFindRequestHandler
1073 {
1074 private:
1075 OrthancPlugins& that_;
1076 std::unique_ptr<DicomArray> currentQuery_;
1077
1078 void Reset()
1079 {
1080 currentQuery_.reset(NULL);
1081 }
1082
1083 public:
1084 FindHandler(OrthancPlugins& that) : that_(that)
1085 {
1086 Reset();
1087 }
1088
1089 virtual void Handle(DicomFindAnswers& answers,
1090 const DicomMap& input,
1091 const std::list<DicomTag>& sequencesToReturn,
1092 const std::string& remoteIp,
1093 const std::string& remoteAet,
1094 const std::string& calledAet,
1095 ModalityManufacturer manufacturer)
1096 {
1097 DicomMap tmp;
1098 tmp.Assign(input);
1099
1100 for (std::list<DicomTag>::const_iterator it = sequencesToReturn.begin();
1101 it != sequencesToReturn.end(); ++it)
1102 {
1103 if (!input.HasTag(*it))
1104 {
1105 tmp.SetValue(*it, "", false);
1106 }
1107 }
1108
1109 {
1110 boost::mutex::scoped_lock lock(that_.pimpl_->findCallbackMutex_);
1111 currentQuery_.reset(new DicomArray(tmp));
1112
1113 if (that_.pimpl_->findCallback_)
1114 {
1115 OrthancPluginErrorCode error = that_.pimpl_->findCallback_
1116 (reinterpret_cast<OrthancPluginFindAnswers*>(&answers),
1117 reinterpret_cast<const OrthancPluginFindQuery*>(this),
1118 remoteAet.c_str(),
1119 calledAet.c_str());
1120
1121 if (error != OrthancPluginErrorCode_Success)
1122 {
1123 Reset();
1124 that_.GetErrorDictionary().LogError(error, true);
1125 throw OrthancException(static_cast<ErrorCode>(error));
1126 }
1127 }
1128
1129 Reset();
1130 }
1131 }
1132
1133 void Invoke(_OrthancPluginService service,
1134 const _OrthancPluginFindOperation& operation) const
1135 {
1136 if (currentQuery_.get() == NULL)
1137 {
1138 throw OrthancException(ErrorCode_Plugin);
1139 }
1140
1141 switch (service)
1142 {
1143 case _OrthancPluginService_GetFindQuerySize:
1144 *operation.resultUint32 = currentQuery_->GetSize();
1145 break;
1146
1147 case _OrthancPluginService_GetFindQueryTag:
1148 {
1149 const DicomTag& tag = currentQuery_->GetElement(operation.index).GetTag();
1150 *operation.resultGroup = tag.GetGroup();
1151 *operation.resultElement = tag.GetElement();
1152 break;
1153 }
1154
1155 case _OrthancPluginService_GetFindQueryTagName:
1156 {
1157 const DicomElement& element = currentQuery_->GetElement(operation.index);
1158 *operation.resultString = CopyString(FromDcmtkBridge::GetTagName(element));
1159 break;
1160 }
1161
1162 case _OrthancPluginService_GetFindQueryValue:
1163 {
1164 *operation.resultString = CopyString(currentQuery_->GetElement(operation.index).GetValue().GetContent());
1165 break;
1166 }
1167
1168 default:
1169 throw OrthancException(ErrorCode_InternalError);
1170 }
1171 }
1172 };
1173
1174
1175
1176 class OrthancPlugins::MoveHandler : public IMoveRequestHandler
1177 {
1178 private:
1179 class Driver : public IMoveRequestIterator
1180 {
1181 private:
1182 void* driver_;
1183 unsigned int count_;
1184 unsigned int pos_;
1185 OrthancPluginApplyMove apply_;
1186 OrthancPluginFreeMove free_;
1187
1188 public:
1189 Driver(void* driver,
1190 unsigned int count,
1191 OrthancPluginApplyMove apply,
1192 OrthancPluginFreeMove free) :
1193 driver_(driver),
1194 count_(count),
1195 pos_(0),
1196 apply_(apply),
1197 free_(free)
1198 {
1199 if (driver_ == NULL)
1200 {
1201 throw OrthancException(ErrorCode_Plugin);
1202 }
1203 }
1204
1205 virtual ~Driver()
1206 {
1207 if (driver_ != NULL)
1208 {
1209 free_(driver_);
1210 driver_ = NULL;
1211 }
1212 }
1213
1214 virtual unsigned int GetSubOperationCount() const
1215 {
1216 return count_;
1217 }
1218
1219 virtual Status DoNext()
1220 {
1221 if (pos_ >= count_)
1222 {
1223 throw OrthancException(ErrorCode_BadSequenceOfCalls);
1224 }
1225 else
1226 {
1227 OrthancPluginErrorCode error = apply_(driver_);
1228 if (error != OrthancPluginErrorCode_Success)
1229 {
1230 LOG(ERROR) << "Error while doing C-Move from plugin: "
1231 << EnumerationToString(static_cast<ErrorCode>(error));
1232 return Status_Failure;
1233 }
1234 else
1235 {
1236 pos_++;
1237 return Status_Success;
1238 }
1239 }
1240 }
1241 };
1242
1243
1244 _OrthancPluginMoveCallback params_;
1245
1246
1247 static std::string ReadTag(const DicomMap& input,
1248 const DicomTag& tag)
1249 {
1250 const DicomValue* value = input.TestAndGetValue(tag);
1251 if (value != NULL &&
1252 !value->IsBinary() &&
1253 !value->IsNull())
1254 {
1255 return value->GetContent();
1256 }
1257 else
1258 {
1259 return std::string();
1260 }
1261 }
1262
1263
1264
1265 public:
1266 MoveHandler(OrthancPlugins& that)
1267 {
1268 boost::recursive_mutex::scoped_lock lock(that.pimpl_->invokeServiceMutex_);
1269 params_ = that.pimpl_->moveCallbacks_;
1270
1271 if (params_.callback == NULL ||
1272 params_.getMoveSize == NULL ||
1273 params_.applyMove == NULL ||
1274 params_.freeMove == NULL)
1275 {
1276 throw OrthancException(ErrorCode_Plugin);
1277 }
1278 }
1279
1280 virtual IMoveRequestIterator* Handle(const std::string& targetAet,
1281 const DicomMap& input,
1282 const std::string& originatorIp,
1283 const std::string& originatorAet,
1284 const std::string& calledAet,
1285 uint16_t originatorId)
1286 {
1287 std::string levelString = ReadTag(input, DICOM_TAG_QUERY_RETRIEVE_LEVEL);
1288 std::string patientId = ReadTag(input, DICOM_TAG_PATIENT_ID);
1289 std::string accessionNumber = ReadTag(input, DICOM_TAG_ACCESSION_NUMBER);
1290 std::string studyInstanceUid = ReadTag(input, DICOM_TAG_STUDY_INSTANCE_UID);
1291 std::string seriesInstanceUid = ReadTag(input, DICOM_TAG_SERIES_INSTANCE_UID);
1292 std::string sopInstanceUid = ReadTag(input, DICOM_TAG_SOP_INSTANCE_UID);
1293
1294 OrthancPluginResourceType level = OrthancPluginResourceType_None;
1295
1296 if (!levelString.empty())
1297 {
1298 level = Plugins::Convert(StringToResourceType(levelString.c_str()));
1299 }
1300
1301 void* driver = params_.callback(level,
1302 patientId.empty() ? NULL : patientId.c_str(),
1303 accessionNumber.empty() ? NULL : accessionNumber.c_str(),
1304 studyInstanceUid.empty() ? NULL : studyInstanceUid.c_str(),
1305 seriesInstanceUid.empty() ? NULL : seriesInstanceUid.c_str(),
1306 sopInstanceUid.empty() ? NULL : sopInstanceUid.c_str(),
1307 originatorAet.c_str(),
1308 calledAet.c_str(),
1309 targetAet.c_str(),
1310 originatorId);
1311
1312 if (driver == NULL)
1313 {
1314 throw OrthancException(ErrorCode_Plugin,
1315 "Plugin cannot create a driver for an incoming C-MOVE request");
1316 }
1317
1318 unsigned int size = params_.getMoveSize(driver);
1319
1320 return new Driver(driver, size, params_.applyMove, params_.freeMove);
1321 }
1322 };
1323
1324
1325
1326 class OrthancPlugins::HttpClientChunkedRequest : public HttpClient::IRequestBody
1327 {
1328 private:
1329 const _OrthancPluginChunkedHttpClient& params_;
1330 PluginsErrorDictionary& errorDictionary_;
1331
1332 public:
1333 HttpClientChunkedRequest(const _OrthancPluginChunkedHttpClient& params,
1334 PluginsErrorDictionary& errorDictionary) :
1335 params_(params),
1336 errorDictionary_(errorDictionary)
1337 {
1338 }
1339
1340 virtual bool ReadNextChunk(std::string& chunk)
1341 {
1342 if (params_.requestIsDone(params_.request))
1343 {
1344 return false;
1345 }
1346 else
1347 {
1348 size_t size = params_.requestChunkSize(params_.request);
1349
1350 chunk.resize(size);
1351
1352 if (size != 0)
1353 {
1354 const void* data = params_.requestChunkData(params_.request);
1355 memcpy(&chunk[0], data, size);
1356 }
1357
1358 OrthancPluginErrorCode error = params_.requestNext(params_.request);
1359
1360 if (error != OrthancPluginErrorCode_Success)
1361 {
1362 errorDictionary_.LogError(error, true);
1363 throw OrthancException(static_cast<ErrorCode>(error));
1364 }
1365 else
1366 {
1367 return true;
1368 }
1369 }
1370 }
1371 };
1372
1373
1374 class OrthancPlugins::HttpClientChunkedAnswer : public HttpClient::IAnswer
1375 {
1376 private:
1377 const _OrthancPluginChunkedHttpClient& params_;
1378 PluginsErrorDictionary& errorDictionary_;
1379
1380 public:
1381 HttpClientChunkedAnswer(const _OrthancPluginChunkedHttpClient& params,
1382 PluginsErrorDictionary& errorDictionary) :
1383 params_(params),
1384 errorDictionary_(errorDictionary)
1385 {
1386 }
1387
1388 virtual void AddHeader(const std::string& key,
1389 const std::string& value)
1390 {
1391 OrthancPluginErrorCode error = params_.answerAddHeader(params_.answer, key.c_str(), value.c_str());
1392
1393 if (error != OrthancPluginErrorCode_Success)
1394 {
1395 errorDictionary_.LogError(error, true);
1396 throw OrthancException(static_cast<ErrorCode>(error));
1397 }
1398 }
1399
1400 virtual void AddChunk(const void* data,
1401 size_t size)
1402 {
1403 OrthancPluginErrorCode error = params_.answerAddChunk(params_.answer, data, size);
1404
1405 if (error != OrthancPluginErrorCode_Success)
1406 {
1407 errorDictionary_.LogError(error, true);
1408 throw OrthancException(static_cast<ErrorCode>(error));
1409 }
1410 }
1411 };
1412
1413
1414 OrthancPlugins::OrthancPlugins()
1415 {
1416 /* Sanity check of the compiler */
1417 if (sizeof(int32_t) != sizeof(OrthancPluginErrorCode) ||
1418 sizeof(int32_t) != sizeof(OrthancPluginHttpMethod) ||
1419 sizeof(int32_t) != sizeof(_OrthancPluginService) ||
1420 sizeof(int32_t) != sizeof(_OrthancPluginProperty) ||
1421 sizeof(int32_t) != sizeof(OrthancPluginPixelFormat) ||
1422 sizeof(int32_t) != sizeof(OrthancPluginContentType) ||
1423 sizeof(int32_t) != sizeof(OrthancPluginResourceType) ||
1424 sizeof(int32_t) != sizeof(OrthancPluginChangeType) ||
1425 sizeof(int32_t) != sizeof(OrthancPluginImageFormat) ||
1426 sizeof(int32_t) != sizeof(OrthancPluginCompressionType) ||
1427 sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation) ||
1428 sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFlags) ||
1429 sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFormat) ||
1430 sizeof(int32_t) != sizeof(OrthancPluginCreateDicomFlags) ||
1431 sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType) ||
1432 sizeof(int32_t) != sizeof(OrthancPluginIdentifierConstraint) ||
1433 sizeof(int32_t) != sizeof(OrthancPluginInstanceOrigin) ||
1434 sizeof(int32_t) != sizeof(OrthancPluginJobStepStatus) ||
1435 sizeof(int32_t) != sizeof(OrthancPluginConstraintType) ||
1436 sizeof(int32_t) != sizeof(OrthancPluginMetricsType) ||
1437 sizeof(int32_t) != sizeof(OrthancPluginDicomWebBinaryMode) ||
1438 sizeof(int32_t) != sizeof(OrthancPluginStorageCommitmentFailureReason) ||
1439 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeBinary) != static_cast<int>(DicomToJsonFlags_IncludeBinary) ||
1440 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePrivateTags) != static_cast<int>(DicomToJsonFlags_IncludePrivateTags) ||
1441 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeUnknownTags) != static_cast<int>(DicomToJsonFlags_IncludeUnknownTags) ||
1442 static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePixelData) != static_cast<int>(DicomToJsonFlags_IncludePixelData) ||
1443 static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToNull) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToNull) ||
1444 static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToAscii) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToAscii) ||
1445 static_cast<int>(OrthancPluginCreateDicomFlags_DecodeDataUriScheme) != static_cast<int>(DicomFromJsonFlags_DecodeDataUriScheme) ||
1446 static_cast<int>(OrthancPluginCreateDicomFlags_GenerateIdentifiers) != static_cast<int>(DicomFromJsonFlags_GenerateIdentifiers))
1447
1448 {
1449 throw OrthancException(ErrorCode_Plugin);
1450 }
1451
1452 pimpl_.reset(new PImpl());
1453 pimpl_->manager_.RegisterServiceProvider(*this);
1454 }
1455
1456
1457 void OrthancPlugins::SetServerContext(ServerContext& context)
1458 {
1459 pimpl_->SetServerContext(&context);
1460 }
1461
1462
1463 void OrthancPlugins::ResetServerContext()
1464 {
1465 pimpl_->SetServerContext(NULL);
1466 }
1467
1468
1469 OrthancPlugins::~OrthancPlugins()
1470 {
1471 for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin();
1472 it != pimpl_->restCallbacks_.end(); ++it)
1473 {
1474 delete *it;
1475 }
1476
1477 for (PImpl::ChunkedRestCallbacks::iterator it = pimpl_->chunkedRestCallbacks_.begin();
1478 it != pimpl_->chunkedRestCallbacks_.end(); ++it)
1479 {
1480 delete *it;
1481 }
1482
1483 for (PImpl::StorageCommitmentScpCallbacks::iterator
1484 it = pimpl_->storageCommitmentScpCallbacks_.begin();
1485 it != pimpl_->storageCommitmentScpCallbacks_.end(); ++it)
1486 {
1487 delete *it;
1488 }
1489 }
1490
1491
1492 static void ArgumentsToPlugin(std::vector<const char*>& keys,
1493 std::vector<const char*>& values,
1494 const IHttpHandler::Arguments& arguments)
1495 {
1496 keys.resize(arguments.size());
1497 values.resize(arguments.size());
1498
1499 size_t pos = 0;
1500 for (IHttpHandler::Arguments::const_iterator
1501 it = arguments.begin(); it != arguments.end(); ++it)
1502 {
1503 keys[pos] = it->first.c_str();
1504 values[pos] = it->second.c_str();
1505 pos++;
1506 }
1507 }
1508
1509
1510 static void ArgumentsToPlugin(std::vector<const char*>& keys,
1511 std::vector<const char*>& values,
1512 const IHttpHandler::GetArguments& arguments)
1513 {
1514 keys.resize(arguments.size());
1515 values.resize(arguments.size());
1516
1517 for (size_t i = 0; i < arguments.size(); i++)
1518 {
1519 keys[i] = arguments[i].first.c_str();
1520 values[i] = arguments[i].second.c_str();
1521 }
1522 }
1523
1524
1525 namespace
1526 {
1527 class RestCallbackMatcher : public boost::noncopyable
1528 {
1529 private:
1530 std::string flatUri_;
1531 std::vector<std::string> groups_;
1532 std::vector<const char*> cgroups_;
1533
1534 public:
1535 RestCallbackMatcher(const UriComponents& uri) :
1536 flatUri_(Toolbox::FlattenUri(uri))
1537 {
1538 }
1539
1540 bool IsMatch(const boost::regex& re)
1541 {
1542 // Check whether the regular expression associated to this
1543 // callback matches the URI
1544 boost::cmatch what;
1545
1546 if (boost::regex_match(flatUri_.c_str(), what, re))
1547 {
1548 // Extract the value of the free parameters of the regular expression
1549 if (what.size() > 1)
1550 {
1551 groups_.resize(what.size() - 1);
1552 cgroups_.resize(what.size() - 1);
1553 for (size_t i = 1; i < what.size(); i++)
1554 {
1555 groups_[i - 1] = what[i];
1556 cgroups_[i - 1] = groups_[i - 1].c_str();
1557 }
1558 }
1559
1560 return true;
1561 }
1562 else
1563 {
1564 // Not a match
1565 return false;
1566 }
1567 }
1568
1569 uint32_t GetGroupsCount() const
1570 {
1571 return cgroups_.size();
1572 }
1573
1574 const char* const* GetGroups() const
1575 {
1576 return cgroups_.empty() ? NULL : &cgroups_[0];
1577 }
1578
1579 const std::string& GetFlatUri() const
1580 {
1581 return flatUri_;
1582 }
1583 };
1584
1585
1586 // WARNING - The lifetime of this internal object must be smaller
1587 // than "matcher", "headers" and "getArguments" objects
1588 class HttpRequestConverter
1589 {
1590 private:
1591 std::vector<const char*> getKeys_;
1592 std::vector<const char*> getValues_;
1593 std::vector<const char*> headersKeys_;
1594 std::vector<const char*> headersValues_;
1595 OrthancPluginHttpRequest converted_;
1596
1597 public:
1598 HttpRequestConverter(const RestCallbackMatcher& matcher,
1599 HttpMethod method,
1600 const IHttpHandler::Arguments& headers)
1601 {
1602 memset(&converted_, 0, sizeof(OrthancPluginHttpRequest));
1603
1604 ArgumentsToPlugin(headersKeys_, headersValues_, headers);
1605 assert(headersKeys_.size() == headersValues_.size());
1606
1607 switch (method)
1608 {
1609 case HttpMethod_Get:
1610 converted_.method = OrthancPluginHttpMethod_Get;
1611 break;
1612
1613 case HttpMethod_Post:
1614 converted_.method = OrthancPluginHttpMethod_Post;
1615 break;
1616
1617 case HttpMethod_Delete:
1618 converted_.method = OrthancPluginHttpMethod_Delete;
1619 break;
1620
1621 case HttpMethod_Put:
1622 converted_.method = OrthancPluginHttpMethod_Put;
1623 break;
1624
1625 default:
1626 throw OrthancException(ErrorCode_InternalError);
1627 }
1628
1629 converted_.groups = matcher.GetGroups();
1630 converted_.groupsCount = matcher.GetGroupsCount();
1631 converted_.getCount = 0;
1632 converted_.getKeys = NULL;
1633 converted_.getValues = NULL;
1634 converted_.body = NULL;
1635 converted_.bodySize = 0;
1636 converted_.headersCount = headers.size();
1637
1638 if (headers.size() > 0)
1639 {
1640 converted_.headersKeys = &headersKeys_[0];
1641 converted_.headersValues = &headersValues_[0];
1642 }
1643 }
1644
1645 void SetGetArguments(const IHttpHandler::GetArguments& getArguments)
1646 {
1647 ArgumentsToPlugin(getKeys_, getValues_, getArguments);
1648 assert(getKeys_.size() == getValues_.size());
1649
1650 converted_.getCount = getArguments.size();
1651
1652 if (getArguments.size() > 0)
1653 {
1654 converted_.getKeys = &getKeys_[0];
1655 converted_.getValues = &getValues_[0];
1656 }
1657 }
1658
1659 OrthancPluginHttpRequest& GetRequest()
1660 {
1661 return converted_;
1662 }
1663 };
1664 }
1665
1666
1667 static std::string GetAllowedMethods(_OrthancPluginChunkedRestCallback parameters)
1668 {
1669 std::string s;
1670
1671 if (parameters.getHandler != NULL)
1672 {
1673 s += "GET";
1674 }
1675
1676 if (parameters.postHandler != NULL)
1677 {
1678 if (!s.empty())
1679 {
1680 s+= ",";
1681 }
1682
1683 s += "POST";
1684 }
1685
1686 if (parameters.deleteHandler != NULL)
1687 {
1688 if (!s.empty())
1689 {
1690 s+= ",";
1691 }
1692
1693 s += "DELETE";
1694 }
1695
1696 if (parameters.putHandler != NULL)
1697 {
1698 if (!s.empty())
1699 {
1700 s+= ",";
1701 }
1702
1703 s += "PUT";
1704 }
1705
1706 return s;
1707 }
1708
1709
1710 bool OrthancPlugins::HandleChunkedGetDelete(HttpOutput& output,
1711 HttpMethod method,
1712 const UriComponents& uri,
1713 const Arguments& headers,
1714 const GetArguments& getArguments)
1715 {
1716 RestCallbackMatcher matcher(uri);
1717
1718 PImpl::ChunkedRestCallback* callback = NULL;
1719
1720 // Loop over the callbacks registered by the plugins
1721 for (PImpl::ChunkedRestCallbacks::const_iterator it = pimpl_->chunkedRestCallbacks_.begin();
1722 it != pimpl_->chunkedRestCallbacks_.end(); ++it)
1723 {
1724 if (matcher.IsMatch((*it)->GetRegularExpression()))
1725 {
1726 callback = *it;
1727 break;
1728 }
1729 }
1730
1731 if (callback == NULL)
1732 {
1733 return false;
1734 }
1735 else
1736 {
1737 LOG(INFO) << "Delegating HTTP request to plugin for URI: " << matcher.GetFlatUri();
1738
1739 OrthancPluginRestCallback handler;
1740
1741 switch (method)
1742 {
1743 case HttpMethod_Get:
1744 handler = callback->GetParameters().getHandler;
1745 break;
1746
1747 case HttpMethod_Delete:
1748 handler = callback->GetParameters().deleteHandler;
1749 break;
1750
1751 default:
1752 handler = NULL;
1753 break;
1754 }
1755
1756 if (handler == NULL)
1757 {
1758 output.SendMethodNotAllowed(GetAllowedMethods(callback->GetParameters()));
1759 }
1760 else
1761 {
1762 HttpRequestConverter converter(matcher, method, headers);
1763 converter.SetGetArguments(getArguments);
1764
1765 PImpl::PluginHttpOutput pluginOutput(output);
1766
1767 OrthancPluginErrorCode error = handler(
1768 reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput),
1769 matcher.GetFlatUri().c_str(), &converter.GetRequest());
1770
1771 pluginOutput.Close(error, GetErrorDictionary());
1772 }
1773
1774 return true;
1775 }
1776 }
1777
1778
1779 bool OrthancPlugins::Handle(HttpOutput& output,
1780 RequestOrigin /*origin*/,
1781 const char* /*remoteIp*/,
1782 const char* /*username*/,
1783 HttpMethod method,
1784 const UriComponents& uri,
1785 const Arguments& headers,
1786 const GetArguments& getArguments,
1787 const void* bodyData,
1788 size_t bodySize)
1789 {
1790 RestCallbackMatcher matcher(uri);
1791
1792 PImpl::RestCallback* callback = NULL;
1793
1794 // Loop over the callbacks registered by the plugins
1795 for (PImpl::RestCallbacks::const_iterator it = pimpl_->restCallbacks_.begin();
1796 it != pimpl_->restCallbacks_.end(); ++it)
1797 {
1798 if (matcher.IsMatch((*it)->GetRegularExpression()))
1799 {
1800 callback = *it;
1801 break;
1802 }
1803 }
1804
1805 if (callback == NULL)
1806 {
1807 // Callback not found, try to find a chunked callback
1808 return HandleChunkedGetDelete(output, method, uri, headers, getArguments);
1809 }
1810
1811 LOG(INFO) << "Delegating HTTP request to plugin for URI: " << matcher.GetFlatUri();
1812
1813 HttpRequestConverter converter(matcher, method, headers);
1814 converter.SetGetArguments(getArguments);
1815 converter.GetRequest().body = bodyData;
1816 converter.GetRequest().bodySize = bodySize;
1817
1818 PImpl::PluginHttpOutput pluginOutput(output);
1819
1820 assert(callback != NULL);
1821 OrthancPluginErrorCode error = callback->Invoke
1822 (pimpl_->restCallbackMutex_, pluginOutput, matcher.GetFlatUri(), converter.GetRequest());
1823
1824 pluginOutput.Close(error, GetErrorDictionary());
1825 return true;
1826 }
1827
1828
1829 class OrthancPlugins::IDicomInstance : public boost::noncopyable
1830 {
1831 public:
1832 virtual ~IDicomInstance()
1833 {
1834 }
1835
1836 virtual bool CanBeFreed() const = 0;
1837
1838 virtual const DicomInstanceToStore& GetInstance() const = 0;
1839 };
1840
1841
1842 class OrthancPlugins::DicomInstanceFromCallback : public IDicomInstance
1843 {
1844 private:
1845 const DicomInstanceToStore& instance_;
1846
1847 public:
1848 DicomInstanceFromCallback(const DicomInstanceToStore& instance) :
1849 instance_(instance)
1850 {
1851 }
1852
1853 virtual bool CanBeFreed() const ORTHANC_OVERRIDE
1854 {
1855 return false;
1856 }
1857
1858 virtual const DicomInstanceToStore& GetInstance() const ORTHANC_OVERRIDE
1859 {
1860 return instance_;
1861 };
1862 };
1863
1864
1865 class OrthancPlugins::DicomInstanceFromBuffer : public IDicomInstance
1866 {
1867 private:
1868 std::string buffer_;
1869 DicomInstanceToStore instance_;
1870
1871 public:
1872 DicomInstanceFromBuffer(const void* buffer,
1873 size_t size)
1874 {
1875 buffer_.assign(reinterpret_cast<const char*>(buffer), size);
1876 instance_.SetBuffer(buffer_.empty() ? NULL : buffer_.c_str(), buffer_.size());
1877 instance_.SetOrigin(DicomInstanceOrigin::FromPlugins());
1878 }
1879
1880 virtual bool CanBeFreed() const ORTHANC_OVERRIDE
1881 {
1882 return true;
1883 }
1884
1885 virtual const DicomInstanceToStore& GetInstance() const ORTHANC_OVERRIDE
1886 {
1887 return instance_;
1888 };
1889 };
1890
1891
1892 class OrthancPlugins::DicomInstanceFromTranscoded : public IDicomInstance
1893 {
1894 private:
1895 std::unique_ptr<ParsedDicomFile> parsed_;
1896 DicomInstanceToStore instance_;
1897
1898 public:
1899 DicomInstanceFromTranscoded(IDicomTranscoder::DicomImage& transcoded) :
1900 parsed_(transcoded.ReleaseAsParsedDicomFile())
1901 {
1902 instance_.SetParsedDicomFile(*parsed_);
1903 instance_.SetOrigin(DicomInstanceOrigin::FromPlugins());
1904 }
1905
1906 virtual bool CanBeFreed() const ORTHANC_OVERRIDE
1907 {
1908 return true;
1909 }
1910
1911 virtual const DicomInstanceToStore& GetInstance() const ORTHANC_OVERRIDE
1912 {
1913 return instance_;
1914 };
1915 };
1916
1917
1918 void OrthancPlugins::SignalStoredInstance(const std::string& instanceId,
1919 const DicomInstanceToStore& instance,
1920 const Json::Value& simplifiedTags)
1921 {
1922 DicomInstanceFromCallback wrapped(instance);
1923
1924 boost::recursive_mutex::scoped_lock lock(pimpl_->storedCallbackMutex_);
1925
1926 for (PImpl::OnStoredCallbacks::const_iterator
1927 callback = pimpl_->onStoredCallbacks_.begin();
1928 callback != pimpl_->onStoredCallbacks_.end(); ++callback)
1929 {
1930 OrthancPluginErrorCode error = (*callback) (
1931 reinterpret_cast<OrthancPluginDicomInstance*>(&wrapped),
1932 instanceId.c_str());
1933
1934 if (error != OrthancPluginErrorCode_Success)
1935 {
1936 GetErrorDictionary().LogError(error, true);
1937 throw OrthancException(static_cast<ErrorCode>(error));
1938 }
1939 }
1940 }
1941
1942
1943 bool OrthancPlugins::FilterIncomingInstance(const DicomInstanceToStore& instance,
1944 const Json::Value& simplified)
1945 {
1946 DicomInstanceFromCallback wrapped(instance);
1947
1948 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
1949
1950 for (PImpl::IncomingDicomInstanceFilters::const_iterator
1951 filter = pimpl_->incomingDicomInstanceFilters_.begin();
1952 filter != pimpl_->incomingDicomInstanceFilters_.end(); ++filter)
1953 {
1954 int32_t allowed = (*filter) (reinterpret_cast<const OrthancPluginDicomInstance*>(&wrapped));
1955
1956 if (allowed == 0)
1957 {
1958 return false;
1959 }
1960 else if (allowed != 1)
1961 {
1962 // The callback is only allowed to answer 0 or 1
1963 throw OrthancException(ErrorCode_Plugin);
1964 }
1965 }
1966
1967 return true;
1968 }
1969
1970
1971 void OrthancPlugins::SignalChangeInternal(OrthancPluginChangeType changeType,
1972 OrthancPluginResourceType resourceType,
1973 const char* resource)
1974 {
1975 boost::recursive_mutex::scoped_lock lock(pimpl_->changeCallbackMutex_);
1976
1977 for (std::list<OrthancPluginOnChangeCallback>::const_iterator
1978 callback = pimpl_->onChangeCallbacks_.begin();
1979 callback != pimpl_->onChangeCallbacks_.end(); ++callback)
1980 {
1981 OrthancPluginErrorCode error = (*callback) (changeType, resourceType, resource);
1982
1983 if (error != OrthancPluginErrorCode_Success)
1984 {
1985 GetErrorDictionary().LogError(error, true);
1986 throw OrthancException(static_cast<ErrorCode>(error));
1987 }
1988 }
1989 }
1990
1991
1992
1993 void OrthancPlugins::SignalChange(const ServerIndexChange& change)
1994 {
1995 SignalChangeInternal(Plugins::Convert(change.GetChangeType()),
1996 Plugins::Convert(change.GetResourceType()),
1997 change.GetPublicId().c_str());
1998 }
1999
2000
2001
2002 void OrthancPlugins::RegisterRestCallback(const void* parameters,
2003 bool lock)
2004 {
2005 const _OrthancPluginRestCallback& p =
2006 *reinterpret_cast<const _OrthancPluginRestCallback*>(parameters);
2007
2008 LOG(INFO) << "Plugin has registered a REST callback "
2009 << (lock ? "with" : "without")
2010 << " mutual exclusion on: "
2011 << p.pathRegularExpression;
2012
2013 pimpl_->restCallbacks_.push_back(new PImpl::RestCallback(p.pathRegularExpression, p.callback, lock));
2014 }
2015
2016
2017 void OrthancPlugins::RegisterChunkedRestCallback(const void* parameters)
2018 {
2019 const _OrthancPluginChunkedRestCallback& p =
2020 *reinterpret_cast<const _OrthancPluginChunkedRestCallback*>(parameters);
2021
2022 LOG(INFO) << "Plugin has registered a REST callback for chunked streams on: "
2023 << p.pathRegularExpression;
2024
2025 pimpl_->chunkedRestCallbacks_.push_back(new PImpl::ChunkedRestCallback(p));
2026 }
2027
2028
2029 void OrthancPlugins::RegisterOnStoredInstanceCallback(const void* parameters)
2030 {
2031 const _OrthancPluginOnStoredInstanceCallback& p =
2032 *reinterpret_cast<const _OrthancPluginOnStoredInstanceCallback*>(parameters);
2033
2034 LOG(INFO) << "Plugin has registered an OnStoredInstance callback";
2035 pimpl_->onStoredCallbacks_.push_back(p.callback);
2036 }
2037
2038
2039 void OrthancPlugins::RegisterOnChangeCallback(const void* parameters)
2040 {
2041 const _OrthancPluginOnChangeCallback& p =
2042 *reinterpret_cast<const _OrthancPluginOnChangeCallback*>(parameters);
2043
2044 LOG(INFO) << "Plugin has registered an OnChange callback";
2045 pimpl_->onChangeCallbacks_.push_back(p.callback);
2046 }
2047
2048
2049 void OrthancPlugins::RegisterWorklistCallback(const void* parameters)
2050 {
2051 const _OrthancPluginWorklistCallback& p =
2052 *reinterpret_cast<const _OrthancPluginWorklistCallback*>(parameters);
2053
2054 boost::mutex::scoped_lock lock(pimpl_->worklistCallbackMutex_);
2055
2056 if (pimpl_->worklistCallback_ != NULL)
2057 {
2058 throw OrthancException(ErrorCode_Plugin,
2059 "Can only register one plugin to handle modality worklists");
2060 }
2061 else
2062 {
2063 LOG(INFO) << "Plugin has registered a callback to handle modality worklists";
2064 pimpl_->worklistCallback_ = p.callback;
2065 }
2066 }
2067
2068
2069 void OrthancPlugins::RegisterFindCallback(const void* parameters)
2070 {
2071 const _OrthancPluginFindCallback& p =
2072 *reinterpret_cast<const _OrthancPluginFindCallback*>(parameters);
2073
2074 boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_);
2075
2076 if (pimpl_->findCallback_ != NULL)
2077 {
2078 throw OrthancException(ErrorCode_Plugin,
2079 "Can only register one plugin to handle C-FIND requests");
2080 }
2081 else
2082 {
2083 LOG(INFO) << "Plugin has registered a callback to handle C-FIND requests";
2084 pimpl_->findCallback_ = p.callback;
2085 }
2086 }
2087
2088
2089 void OrthancPlugins::RegisterMoveCallback(const void* parameters)
2090 {
2091 // invokeServiceMutex_ is assumed to be locked
2092
2093 const _OrthancPluginMoveCallback& p =
2094 *reinterpret_cast<const _OrthancPluginMoveCallback*>(parameters);
2095
2096 if (pimpl_->moveCallbacks_.callback != NULL)
2097 {
2098 throw OrthancException(ErrorCode_Plugin,
2099 "Can only register one plugin to handle C-MOVE requests");
2100 }
2101 else
2102 {
2103 LOG(INFO) << "Plugin has registered a callback to handle C-MOVE requests";
2104 pimpl_->moveCallbacks_ = p;
2105 }
2106 }
2107
2108
2109 void OrthancPlugins::RegisterDecodeImageCallback(const void* parameters)
2110 {
2111 const _OrthancPluginDecodeImageCallback& p =
2112 *reinterpret_cast<const _OrthancPluginDecodeImageCallback*>(parameters);
2113
2114 boost::unique_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
2115
2116 pimpl_->decodeImageCallbacks_.push_back(p.callback);
2117 LOG(INFO) << "Plugin has registered a callback to decode DICOM images ("
2118 << pimpl_->decodeImageCallbacks_.size() << " decoder(s) now active)";
2119 }
2120
2121
2122 void OrthancPlugins::RegisterTranscoderCallback(const void* parameters)
2123 {
2124 const _OrthancPluginTranscoderCallback& p =
2125 *reinterpret_cast<const _OrthancPluginTranscoderCallback*>(parameters);
2126
2127 boost::unique_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
2128
2129 pimpl_->transcoderCallbacks_.push_back(p.callback);
2130 LOG(INFO) << "Plugin has registered a callback to transcode DICOM images ("
2131 << pimpl_->transcoderCallbacks_.size() << " transcoder(s) now active)";
2132 }
2133
2134
2135 void OrthancPlugins::RegisterJobsUnserializer(const void* parameters)
2136 {
2137 const _OrthancPluginJobsUnserializer& p =
2138 *reinterpret_cast<const _OrthancPluginJobsUnserializer*>(parameters);
2139
2140 boost::mutex::scoped_lock lock(pimpl_->jobsUnserializersMutex_);
2141
2142 pimpl_->jobsUnserializers_.push_back(p.unserializer);
2143 LOG(INFO) << "Plugin has registered a callback to unserialize jobs ("
2144 << pimpl_->jobsUnserializers_.size() << " unserializer(s) now active)";
2145 }
2146
2147
2148 void OrthancPlugins::RegisterIncomingHttpRequestFilter(const void* parameters)
2149 {
2150 const _OrthancPluginIncomingHttpRequestFilter& p =
2151 *reinterpret_cast<const _OrthancPluginIncomingHttpRequestFilter*>(parameters);
2152
2153 LOG(INFO) << "Plugin has registered a callback to filter incoming HTTP requests";
2154 pimpl_->incomingHttpRequestFilters_.push_back(p.callback);
2155 }
2156
2157
2158 void OrthancPlugins::RegisterIncomingHttpRequestFilter2(const void* parameters)
2159 {
2160 const _OrthancPluginIncomingHttpRequestFilter2& p =
2161 *reinterpret_cast<const _OrthancPluginIncomingHttpRequestFilter2*>(parameters);
2162
2163 LOG(INFO) << "Plugin has registered a callback to filter incoming HTTP requests";
2164 pimpl_->incomingHttpRequestFilters2_.push_back(p.callback);
2165 }
2166
2167
2168 void OrthancPlugins::RegisterIncomingDicomInstanceFilter(const void* parameters)
2169 {
2170 const _OrthancPluginIncomingDicomInstanceFilter& p =
2171 *reinterpret_cast<const _OrthancPluginIncomingDicomInstanceFilter*>(parameters);
2172
2173 LOG(INFO) << "Plugin has registered a callback to filter incoming DICOM instances";
2174 pimpl_->incomingDicomInstanceFilters_.push_back(p.callback);
2175 }
2176
2177
2178 void OrthancPlugins::RegisterRefreshMetricsCallback(const void* parameters)
2179 {
2180 const _OrthancPluginRegisterRefreshMetricsCallback& p =
2181 *reinterpret_cast<const _OrthancPluginRegisterRefreshMetricsCallback*>(parameters);
2182
2183 boost::mutex::scoped_lock lock(pimpl_->refreshMetricsMutex_);
2184
2185 LOG(INFO) << "Plugin has registered a callback to refresh its metrics";
2186 pimpl_->refreshMetricsCallbacks_.push_back(p.callback);
2187 }
2188
2189
2190 void OrthancPlugins::RegisterStorageCommitmentScpCallback(const void* parameters)
2191 {
2192 const _OrthancPluginRegisterStorageCommitmentScpCallback& p =
2193 *reinterpret_cast<const _OrthancPluginRegisterStorageCommitmentScpCallback*>(parameters);
2194
2195 boost::mutex::scoped_lock lock(pimpl_->storageCommitmentScpMutex_);
2196 LOG(INFO) << "Plugin has registered a storage commitment callback";
2197
2198 pimpl_->storageCommitmentScpCallbacks_.push_back(new PImpl::StorageCommitmentScp(p));
2199 }
2200
2201
2202 void OrthancPlugins::AnswerBuffer(const void* parameters)
2203 {
2204 const _OrthancPluginAnswerBuffer& p =
2205 *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters);
2206
2207 HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2208 translatedOutput.SetContentType(p.mimeType);
2209 translatedOutput.Answer(p.answer, p.answerSize);
2210 }
2211
2212
2213 void OrthancPlugins::Redirect(const void* parameters)
2214 {
2215 const _OrthancPluginOutputPlusArgument& p =
2216 *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters);
2217
2218 HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2219 translatedOutput.Redirect(p.argument);
2220 }
2221
2222
2223 void OrthancPlugins::SendHttpStatusCode(const void* parameters)
2224 {
2225 const _OrthancPluginSendHttpStatusCode& p =
2226 *reinterpret_cast<const _OrthancPluginSendHttpStatusCode*>(parameters);
2227
2228 HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2229 translatedOutput.SendStatus(static_cast<HttpStatus>(p.status));
2230 }
2231
2232
2233 void OrthancPlugins::SendHttpStatus(const void* parameters)
2234 {
2235 const _OrthancPluginSendHttpStatus& p =
2236 *reinterpret_cast<const _OrthancPluginSendHttpStatus*>(parameters);
2237
2238 HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2239 HttpStatus status = static_cast<HttpStatus>(p.status);
2240
2241 if (p.bodySize > 0 && p.body != NULL)
2242 {
2243 translatedOutput.SendStatus(status, p.body, p.bodySize);
2244 }
2245 else
2246 {
2247 translatedOutput.SendStatus(status);
2248 }
2249 }
2250
2251
2252 void OrthancPlugins::SendUnauthorized(const void* parameters)
2253 {
2254 const _OrthancPluginOutputPlusArgument& p =
2255 *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters);
2256
2257 HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2258 translatedOutput.SendUnauthorized(p.argument);
2259 }
2260
2261
2262 void OrthancPlugins::SendMethodNotAllowed(const void* parameters)
2263 {
2264 const _OrthancPluginOutputPlusArgument& p =
2265 *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters);
2266
2267 HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2268 translatedOutput.SendMethodNotAllowed(p.argument);
2269 }
2270
2271
2272 void OrthancPlugins::SetCookie(const void* parameters)
2273 {
2274 const _OrthancPluginSetHttpHeader& p =
2275 *reinterpret_cast<const _OrthancPluginSetHttpHeader*>(parameters);
2276
2277 HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2278 translatedOutput.SetCookie(p.key, p.value);
2279 }
2280
2281
2282 void OrthancPlugins::SetHttpHeader(const void* parameters)
2283 {
2284 const _OrthancPluginSetHttpHeader& p =
2285 *reinterpret_cast<const _OrthancPluginSetHttpHeader*>(parameters);
2286
2287 HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2288 translatedOutput.AddHeader(p.key, p.value);
2289 }
2290
2291
2292 void OrthancPlugins::SetHttpErrorDetails(const void* parameters)
2293 {
2294 const _OrthancPluginSetHttpErrorDetails& p =
2295 *reinterpret_cast<const _OrthancPluginSetHttpErrorDetails*>(parameters);
2296
2297 PImpl::PluginHttpOutput* output =
2298 reinterpret_cast<PImpl::PluginHttpOutput*>(p.output);
2299 output->SetErrorDetails(p.details, p.log);
2300 }
2301
2302
2303 void OrthancPlugins::CompressAndAnswerPngImage(const void* parameters)
2304 {
2305 // Bridge for backward compatibility with Orthanc <= 0.9.3
2306 const _OrthancPluginCompressAndAnswerPngImage& p =
2307 *reinterpret_cast<const _OrthancPluginCompressAndAnswerPngImage*>(parameters);
2308
2309 _OrthancPluginCompressAndAnswerImage p2;
2310 p2.output = p.output;
2311 p2.imageFormat = OrthancPluginImageFormat_Png;
2312 p2.pixelFormat = p.format;
2313 p2.width = p.width;
2314 p2.height = p.height;
2315 p2.pitch = p.height;
2316 p2.buffer = p.buffer;
2317 p2.quality = 0;
2318
2319 CompressAndAnswerImage(&p2);
2320 }
2321
2322
2323 void OrthancPlugins::CompressAndAnswerImage(const void* parameters)
2324 {
2325 const _OrthancPluginCompressAndAnswerImage& p =
2326 *reinterpret_cast<const _OrthancPluginCompressAndAnswerImage*>(parameters);
2327
2328 HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2329
2330 ImageAccessor accessor;
2331 accessor.AssignReadOnly(Plugins::Convert(p.pixelFormat), p.width, p.height, p.pitch, p.buffer);
2332
2333 std::string compressed;
2334
2335 switch (p.imageFormat)
2336 {
2337 case OrthancPluginImageFormat_Png:
2338 {
2339 PngWriter writer;
2340 writer.WriteToMemory(compressed, accessor);
2341 translatedOutput.SetContentType(MimeType_Png);
2342 break;
2343 }
2344
2345 case OrthancPluginImageFormat_Jpeg:
2346 {
2347 JpegWriter writer;
2348 writer.SetQuality(p.quality);
2349 writer.WriteToMemory(compressed, accessor);
2350 translatedOutput.SetContentType(MimeType_Jpeg);
2351 break;
2352 }
2353
2354 default:
2355 throw OrthancException(ErrorCode_ParameterOutOfRange);
2356 }
2357
2358 translatedOutput.Answer(compressed);
2359 }
2360
2361
2362 void OrthancPlugins::GetDicomForInstance(const void* parameters)
2363 {
2364 const _OrthancPluginGetDicomForInstance& p =
2365 *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters);
2366
2367 std::string dicom;
2368
2369 {
2370 PImpl::ServerContextLock lock(*pimpl_);
2371 lock.GetContext().ReadDicom(dicom, p.instanceId);
2372 }
2373
2374 CopyToMemoryBuffer(*p.target, dicom);
2375 }
2376
2377
2378 void OrthancPlugins::RestApiGet(const void* parameters,
2379 bool afterPlugins)
2380 {
2381 const _OrthancPluginRestApiGet& p =
2382 *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters);
2383
2384 LOG(INFO) << "Plugin making REST GET call on URI " << p.uri
2385 << (afterPlugins ? " (after plugins)" : " (built-in API)");
2386
2387 IHttpHandler* handler;
2388
2389 {
2390 PImpl::ServerContextLock lock(*pimpl_);
2391 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
2392 }
2393
2394 std::map<std::string, std::string> httpHeaders;
2395
2396 std::string result;
2397 if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri, httpHeaders))
2398 {
2399 CopyToMemoryBuffer(*p.target, result);
2400 }
2401 else
2402 {
2403 throw OrthancException(ErrorCode_UnknownResource);
2404 }
2405 }
2406
2407
2408 void OrthancPlugins::RestApiGet2(const void* parameters)
2409 {
2410 const _OrthancPluginRestApiGet2& p =
2411 *reinterpret_cast<const _OrthancPluginRestApiGet2*>(parameters);
2412
2413 LOG(INFO) << "Plugin making REST GET call on URI " << p.uri
2414 << (p.afterPlugins ? " (after plugins)" : " (built-in API)");
2415
2416 IHttpHandler::Arguments headers;
2417
2418 for (uint32_t i = 0; i < p.headersCount; i++)
2419 {
2420 std::string name(p.headersKeys[i]);
2421 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
2422 headers[name] = p.headersValues[i];
2423 }
2424
2425 IHttpHandler* handler;
2426
2427 {
2428 PImpl::ServerContextLock lock(*pimpl_);
2429 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins);
2430 }
2431
2432 std::string result;
2433 if (HttpToolbox::SimpleGet(result, *handler, RequestOrigin_Plugins, p.uri, headers))
2434 {
2435 CopyToMemoryBuffer(*p.target, result);
2436 }
2437 else
2438 {
2439 throw OrthancException(ErrorCode_UnknownResource);
2440 }
2441 }
2442
2443
2444 void OrthancPlugins::RestApiPostPut(bool isPost,
2445 const void* parameters,
2446 bool afterPlugins)
2447 {
2448 const _OrthancPluginRestApiPostPut& p =
2449 *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters);
2450
2451 LOG(INFO) << "Plugin making REST " << EnumerationToString(isPost ? HttpMethod_Post : HttpMethod_Put)
2452 << " call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)");
2453
2454 IHttpHandler* handler;
2455
2456 {
2457 PImpl::ServerContextLock lock(*pimpl_);
2458 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
2459 }
2460
2461 std::map<std::string, std::string> httpHeaders;
2462
2463 std::string result;
2464 if (isPost ?
2465 HttpToolbox::SimplePost(result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize, httpHeaders) :
2466 HttpToolbox::SimplePut (result, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize, httpHeaders))
2467 {
2468 CopyToMemoryBuffer(*p.target, result);
2469 }
2470 else
2471 {
2472 throw OrthancException(ErrorCode_UnknownResource);
2473 }
2474 }
2475
2476
2477 void OrthancPlugins::RestApiDelete(const void* parameters,
2478 bool afterPlugins)
2479 {
2480 const char* uri = reinterpret_cast<const char*>(parameters);
2481 LOG(INFO) << "Plugin making REST DELETE call on URI " << uri
2482 << (afterPlugins ? " (after plugins)" : " (built-in API)");
2483
2484 IHttpHandler* handler;
2485
2486 {
2487 PImpl::ServerContextLock lock(*pimpl_);
2488 handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
2489 }
2490
2491 std::map<std::string, std::string> httpHeaders;
2492
2493 if (!HttpToolbox::SimpleDelete(*handler, RequestOrigin_Plugins, uri, httpHeaders))
2494 {
2495 throw OrthancException(ErrorCode_UnknownResource);
2496 }
2497 }
2498
2499
2500 void OrthancPlugins::LookupResource(_OrthancPluginService service,
2501 const void* parameters)
2502 {
2503 const _OrthancPluginRetrieveDynamicString& p =
2504 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters);
2505
2506 /**
2507 * The enumeration below only uses the tags that are indexed in
2508 * the Orthanc database. It reflects the
2509 * "CandidateResources::ApplyFilter()" method of the
2510 * "OrthancFindRequestHandler" class.
2511 **/
2512
2513 DicomTag tag(0, 0);
2514 ResourceType level;
2515 switch (service)
2516 {
2517 case _OrthancPluginService_LookupPatient:
2518 tag = DICOM_TAG_PATIENT_ID;
2519 level = ResourceType_Patient;
2520 break;
2521
2522 case _OrthancPluginService_LookupStudy:
2523 tag = DICOM_TAG_STUDY_INSTANCE_UID;
2524 level = ResourceType_Study;
2525 break;
2526
2527 case _OrthancPluginService_LookupStudyWithAccessionNumber:
2528 tag = DICOM_TAG_ACCESSION_NUMBER;
2529 level = ResourceType_Study;
2530 break;
2531
2532 case _OrthancPluginService_LookupSeries:
2533 tag = DICOM_TAG_SERIES_INSTANCE_UID;
2534 level = ResourceType_Series;
2535 break;
2536
2537 case _OrthancPluginService_LookupInstance:
2538 tag = DICOM_TAG_SOP_INSTANCE_UID;
2539 level = ResourceType_Instance;
2540 break;
2541
2542 default:
2543 throw OrthancException(ErrorCode_InternalError);
2544 }
2545
2546 std::vector<std::string> result;
2547
2548 {
2549 PImpl::ServerContextLock lock(*pimpl_);
2550 lock.GetContext().GetIndex().LookupIdentifierExact(result, level, tag, p.argument);
2551 }
2552
2553 if (result.size() == 1)
2554 {
2555 *p.result = CopyString(result[0]);
2556 }
2557 else
2558 {
2559 if (result.size() > 1)
2560 {
2561 LOG(WARNING) << "LookupResource(): Multiple resources match the query (instead of 0 or 1), which indicates "
2562 << "your DICOM database breaks the DICOM model of the real world";
2563 }
2564
2565 throw OrthancException(ErrorCode_UnknownResource);
2566 }
2567 }
2568
2569
2570 static void AccessInstanceMetadataInternal(bool checkExistence,
2571 const _OrthancPluginAccessDicomInstance& params,
2572 const DicomInstanceToStore& instance)
2573 {
2574 MetadataType metadata;
2575
2576 try
2577 {
2578 metadata = StringToMetadata(params.key);
2579 }
2580 catch (OrthancException&)
2581 {
2582 // Unknown metadata
2583 if (checkExistence)
2584 {
2585 *params.resultInt64 = -1;
2586 }
2587 else
2588 {
2589 *params.resultString = NULL;
2590 }
2591
2592 return;
2593 }
2594
2595 ServerIndex::MetadataMap::const_iterator it =
2596 instance.GetMetadata().find(std::make_pair(ResourceType_Instance, metadata));
2597
2598 if (checkExistence)
2599 {
2600 if (it != instance.GetMetadata().end())
2601 {
2602 *params.resultInt64 = 1;
2603 }
2604 else
2605 {
2606 *params.resultInt64 = 0;
2607 }
2608 }
2609 else
2610 {
2611 if (it != instance.GetMetadata().end())
2612 {
2613 *params.resultString = it->second.c_str();
2614 }
2615 else
2616 {
2617 // Error: Missing metadata
2618 *params.resultString = NULL;
2619 }
2620 }
2621 }
2622
2623
2624 void OrthancPlugins::AccessDicomInstance(_OrthancPluginService service,
2625 const void* parameters)
2626 {
2627 const _OrthancPluginAccessDicomInstance& p =
2628 *reinterpret_cast<const _OrthancPluginAccessDicomInstance*>(parameters);
2629
2630 if (p.instance == NULL)
2631 {
2632 throw OrthancException(ErrorCode_NullPointer);
2633 }
2634
2635 const DicomInstanceToStore& instance =
2636 reinterpret_cast<const IDicomInstance*>(p.instance)->GetInstance();
2637
2638 switch (service)
2639 {
2640 case _OrthancPluginService_GetInstanceRemoteAet:
2641 *p.resultString = instance.GetOrigin().GetRemoteAetC();
2642 return;
2643
2644 case _OrthancPluginService_GetInstanceSize:
2645 *p.resultInt64 = instance.GetBufferSize();
2646 return;
2647
2648 case _OrthancPluginService_GetInstanceData:
2649 *p.resultString = reinterpret_cast<const char*>(instance.GetBufferData());
2650 return;
2651
2652 case _OrthancPluginService_HasInstanceMetadata:
2653 AccessInstanceMetadataInternal(true, p, instance);
2654 return;
2655
2656 case _OrthancPluginService_GetInstanceMetadata:
2657 AccessInstanceMetadataInternal(false, p, instance);
2658 return;
2659
2660 case _OrthancPluginService_GetInstanceJson:
2661 case _OrthancPluginService_GetInstanceSimplifiedJson:
2662 {
2663 Json::StyledWriter writer;
2664 std::string s;
2665
2666 if (service == _OrthancPluginService_GetInstanceJson)
2667 {
2668 s = writer.write(instance.GetJson());
2669 }
2670 else
2671 {
2672 Json::Value simplified;
2673 ServerToolbox::SimplifyTags(simplified, instance.GetJson(), DicomToJsonFormat_Human);
2674 s = writer.write(simplified);
2675 }
2676
2677 *p.resultStringToFree = CopyString(s);
2678 return;
2679 }
2680
2681 case _OrthancPluginService_GetInstanceOrigin: // New in Orthanc 0.9.5
2682 *p.resultOrigin = Plugins::Convert(instance.GetOrigin().GetRequestOrigin());
2683 return;
2684
2685 case _OrthancPluginService_GetInstanceTransferSyntaxUid: // New in Orthanc 1.6.1
2686 {
2687 std::string s;
2688 if (!instance.LookupTransferSyntax(s))
2689 {
2690 s.clear();
2691 }
2692
2693 *p.resultStringToFree = CopyString(s);
2694 return;
2695 }
2696
2697 case _OrthancPluginService_HasInstancePixelData: // New in Orthanc 1.6.1
2698 *p.resultInt64 = instance.HasPixelData();
2699 return;
2700
2701 case _OrthancPluginService_GetInstanceFramesCount: // New in Orthanc 1.7.0
2702 *p.resultInt64 = instance.GetParsedDicomFile().GetFramesCount();
2703 return;
2704
2705 default:
2706 throw OrthancException(ErrorCode_InternalError);
2707 }
2708 }
2709
2710
2711 void OrthancPlugins::BufferCompression(const void* parameters)
2712 {
2713 const _OrthancPluginBufferCompression& p =
2714 *reinterpret_cast<const _OrthancPluginBufferCompression*>(parameters);
2715
2716 std::string result;
2717
2718 {
2719 std::unique_ptr<DeflateBaseCompressor> compressor;
2720
2721 switch (p.compression)
2722 {
2723 case OrthancPluginCompressionType_Zlib:
2724 {
2725 compressor.reset(new ZlibCompressor);
2726 compressor->SetPrefixWithUncompressedSize(false);
2727 break;
2728 }
2729
2730 case OrthancPluginCompressionType_ZlibWithSize:
2731 {
2732 compressor.reset(new ZlibCompressor);
2733 compressor->SetPrefixWithUncompressedSize(true);
2734 break;
2735 }
2736
2737 case OrthancPluginCompressionType_Gzip:
2738 {
2739 compressor.reset(new GzipCompressor);
2740 compressor->SetPrefixWithUncompressedSize(false);
2741 break;
2742 }
2743
2744 case OrthancPluginCompressionType_GzipWithSize:
2745 {
2746 compressor.reset(new GzipCompressor);
2747 compressor->SetPrefixWithUncompressedSize(true);
2748 break;
2749 }
2750
2751 default:
2752 throw OrthancException(ErrorCode_ParameterOutOfRange);
2753 }
2754
2755 if (p.uncompress)
2756 {
2757 compressor->Uncompress(result, p.source, p.size);
2758 }
2759 else
2760 {
2761 compressor->Compress(result, p.source, p.size);
2762 }
2763 }
2764
2765 CopyToMemoryBuffer(*p.target, result);
2766 }
2767
2768
2769 static OrthancPluginImage* ReturnImage(std::unique_ptr<ImageAccessor>& image)
2770 {
2771 // Images returned to plugins are assumed to be writeable. If the
2772 // input image is read-only, we return a copy so that it can be modified.
2773
2774 if (image.get() == NULL)
2775 {
2776 throw OrthancException(ErrorCode_NullPointer);
2777 }
2778
2779 if (image->IsReadOnly())
2780 {
2781 std::unique_ptr<Image> copy(new Image(image->GetFormat(), image->GetWidth(), image->GetHeight(), false));
2782 ImageProcessing::Copy(*copy, *image);
2783 image.reset(NULL);
2784 return reinterpret_cast<OrthancPluginImage*>(copy.release());
2785 }
2786 else
2787 {
2788 return reinterpret_cast<OrthancPluginImage*>(image.release());
2789 }
2790 }
2791
2792
2793 void OrthancPlugins::AccessDicomInstance2(_OrthancPluginService service,
2794 const void* parameters)
2795 {
2796 const _OrthancPluginAccessDicomInstance2& p =
2797 *reinterpret_cast<const _OrthancPluginAccessDicomInstance2*>(parameters);
2798
2799 if (p.instance == NULL)
2800 {
2801 throw OrthancException(ErrorCode_NullPointer);
2802 }
2803
2804 const DicomInstanceToStore& instance =
2805 reinterpret_cast<const IDicomInstance*>(p.instance)->GetInstance();
2806
2807 switch (service)
2808 {
2809 case _OrthancPluginService_GetInstanceFramesCount:
2810 *p.targetUint32 = instance.GetParsedDicomFile().GetFramesCount();
2811 return;
2812
2813 case _OrthancPluginService_GetInstanceRawFrame:
2814 {
2815 if (p.targetBuffer == NULL)
2816 {
2817 throw OrthancException(ErrorCode_NullPointer);
2818 }
2819
2820 p.targetBuffer->data = NULL;
2821 p.targetBuffer->size = 0;
2822
2823 MimeType mime;
2824 std::string frame;
2825 instance.GetParsedDicomFile().GetRawFrame(frame, mime, p.frameIndex);
2826 CopyToMemoryBuffer(*p.targetBuffer, frame);
2827 return;
2828 }
2829
2830 case _OrthancPluginService_GetInstanceDecodedFrame:
2831 {
2832 if (p.targetImage == NULL)
2833 {
2834 throw OrthancException(ErrorCode_NullPointer);
2835 }
2836
2837 std::unique_ptr<ImageAccessor> decoded;
2838 {
2839 PImpl::ServerContextLock lock(*pimpl_);
2840 decoded.reset(lock.GetContext().DecodeDicomFrame(instance, p.frameIndex));
2841 }
2842
2843 *(p.targetImage) = ReturnImage(decoded);
2844 return;
2845 }
2846
2847 case _OrthancPluginService_SerializeDicomInstance:
2848 {
2849 if (p.targetBuffer == NULL)
2850 {
2851 throw OrthancException(ErrorCode_NullPointer);
2852 }
2853
2854 p.targetBuffer->data = NULL;
2855 p.targetBuffer->size = 0;
2856
2857 std::string serialized;
2858 instance.GetParsedDicomFile().SaveToMemoryBuffer(serialized);
2859 CopyToMemoryBuffer(*p.targetBuffer, serialized);
2860 return;
2861 }
2862
2863 case _OrthancPluginService_GetInstanceAdvancedJson:
2864 {
2865 if (p.targetStringToFree == NULL)
2866 {
2867 throw OrthancException(ErrorCode_NullPointer);
2868 }
2869
2870 Json::Value json;
2871 instance.GetParsedDicomFile().DatasetToJson(
2872 json, Plugins::Convert(p.format),
2873 static_cast<DicomToJsonFlags>(p.flags), p.maxStringLength);
2874
2875 Json::FastWriter writer;
2876 *p.targetStringToFree = CopyString(writer.write(json));
2877 return;
2878 }
2879
2880 case _OrthancPluginService_GetInstanceDicomWebJson:
2881 case _OrthancPluginService_GetInstanceDicomWebXml:
2882 {
2883 if (p.targetStringToFree == NULL)
2884 {
2885 throw OrthancException(ErrorCode_NullPointer);
2886 }
2887
2888 DicomWebBinaryFormatter formatter(p.dicomWebCallback, p.dicomWebPayload);
2889 formatter.Apply(p.targetStringToFree,
2890 (service == _OrthancPluginService_GetInstanceDicomWebJson),
2891 instance.GetParsedDicomFile());
2892 return;
2893 }
2894
2895 default:
2896 throw OrthancException(ErrorCode_InternalError);
2897 }
2898 }
2899
2900
2901 void OrthancPlugins::UncompressImage(const void* parameters)
2902 {
2903 const _OrthancPluginUncompressImage& p = *reinterpret_cast<const _OrthancPluginUncompressImage*>(parameters);
2904
2905 std::unique_ptr<ImageAccessor> image;
2906
2907 switch (p.format)
2908 {
2909 case OrthancPluginImageFormat_Png:
2910 {
2911 image.reset(new PngReader);
2912 reinterpret_cast<PngReader&>(*image).ReadFromMemory(p.data, p.size);
2913 break;
2914 }
2915
2916 case OrthancPluginImageFormat_Jpeg:
2917 {
2918 image.reset(new JpegReader);
2919 reinterpret_cast<JpegReader&>(*image).ReadFromMemory(p.data, p.size);
2920 break;
2921 }
2922
2923 case OrthancPluginImageFormat_Dicom:
2924 {
2925 PImpl::ServerContextLock lock(*pimpl_);
2926 image.reset(lock.GetContext().DecodeDicomFrame(p.data, p.size, 0));
2927 break;
2928 }
2929
2930 default:
2931 throw OrthancException(ErrorCode_ParameterOutOfRange);
2932 }
2933
2934 *(p.target) = ReturnImage(image);
2935 }
2936
2937
2938 void OrthancPlugins::CompressImage(const void* parameters)
2939 {
2940 const _OrthancPluginCompressImage& p = *reinterpret_cast<const _OrthancPluginCompressImage*>(parameters);
2941
2942 std::string compressed;
2943
2944 ImageAccessor accessor;
2945 accessor.AssignReadOnly(Plugins::Convert(p.pixelFormat), p.width, p.height, p.pitch, p.buffer);
2946
2947 switch (p.imageFormat)
2948 {
2949 case OrthancPluginImageFormat_Png:
2950 {
2951 PngWriter writer;
2952 writer.WriteToMemory(compressed, accessor);
2953 break;
2954 }
2955
2956 case OrthancPluginImageFormat_Jpeg:
2957 {
2958 JpegWriter writer;
2959 writer.SetQuality(p.quality);
2960 writer.WriteToMemory(compressed, accessor);
2961 break;
2962 }
2963
2964 default:
2965 throw OrthancException(ErrorCode_ParameterOutOfRange);
2966 }
2967
2968 CopyToMemoryBuffer(*p.target, compressed.size() > 0 ? compressed.c_str() : NULL, compressed.size());
2969 }
2970
2971
2972 static void SetupHttpClient(HttpClient& client,
2973 const _OrthancPluginCallHttpClient2& parameters)
2974 {
2975 client.SetUrl(parameters.url);
2976 client.SetConvertHeadersToLowerCase(false);
2977
2978 if (parameters.timeout != 0)
2979 {
2980 client.SetTimeout(parameters.timeout);
2981 }
2982
2983 if (parameters.username != NULL &&
2984 parameters.password != NULL)
2985 {
2986 client.SetCredentials(parameters.username, parameters.password);
2987 }
2988
2989 if (parameters.certificateFile != NULL)
2990 {
2991 std::string certificate(parameters.certificateFile);
2992 std::string key, password;
2993
2994 if (parameters.certificateKeyFile)
2995 {
2996 key.assign(parameters.certificateKeyFile);
2997 }
2998
2999 if (parameters.certificateKeyPassword)
3000 {
3001 password.assign(parameters.certificateKeyPassword);
3002 }
3003
3004 client.SetClientCertificate(certificate, key, password);
3005 }
3006
3007 client.SetPkcs11Enabled(parameters.pkcs11 ? true : false);
3008
3009 for (uint32_t i = 0; i < parameters.headersCount; i++)
3010 {
3011 if (parameters.headersKeys[i] == NULL ||
3012 parameters.headersValues[i] == NULL)
3013 {
3014 throw OrthancException(ErrorCode_NullPointer);
3015 }
3016
3017 client.AddHeader(parameters.headersKeys[i], parameters.headersValues[i]);
3018 }
3019
3020 switch (parameters.method)
3021 {
3022 case OrthancPluginHttpMethod_Get:
3023 client.SetMethod(HttpMethod_Get);
3024 break;
3025
3026 case OrthancPluginHttpMethod_Post:
3027 client.SetMethod(HttpMethod_Post);
3028 client.GetBody().assign(reinterpret_cast<const char*>(parameters.body), parameters.bodySize);
3029 break;
3030
3031 case OrthancPluginHttpMethod_Put:
3032 client.SetMethod(HttpMethod_Put);
3033 client.GetBody().assign(reinterpret_cast<const char*>(parameters.body), parameters.bodySize);
3034 break;
3035
3036 case OrthancPluginHttpMethod_Delete:
3037 client.SetMethod(HttpMethod_Delete);
3038 break;
3039
3040 default:
3041 throw OrthancException(ErrorCode_ParameterOutOfRange);
3042 }
3043 }
3044
3045
3046 static void ExecuteHttpClientWithoutChunkedBody(uint16_t& httpStatus,
3047 OrthancPluginMemoryBuffer* answerBody,
3048 OrthancPluginMemoryBuffer* answerHeaders,
3049 HttpClient& client)
3050 {
3051 if (answerBody == NULL)
3052 {
3053 throw OrthancException(ErrorCode_NullPointer);
3054 }
3055
3056 std::string body;
3057 HttpClient::HttpHeaders headers;
3058
3059 bool success = client.Apply(body, headers);
3060
3061 // The HTTP request has succeeded
3062 httpStatus = static_cast<uint16_t>(client.GetLastStatus());
3063
3064 if (!success)
3065 {
3066 HttpClient::ThrowException(client.GetLastStatus());
3067 }
3068
3069 // Copy the HTTP headers of the answer, if the plugin requested them
3070 if (answerHeaders != NULL)
3071 {
3072 Json::Value json = Json::objectValue;
3073
3074 for (HttpClient::HttpHeaders::const_iterator
3075 it = headers.begin(); it != headers.end(); ++it)
3076 {
3077 json[it->first] = it->second;
3078 }
3079
3080 std::string s = json.toStyledString();
3081 CopyToMemoryBuffer(*answerHeaders, s);
3082 }
3083
3084 // Copy the body of the answer if it makes sense
3085 if (client.GetMethod() != HttpMethod_Delete)
3086 {
3087 CopyToMemoryBuffer(*answerBody, body);
3088 }
3089 }
3090
3091
3092 void OrthancPlugins::CallHttpClient(const void* parameters)
3093 {
3094 const _OrthancPluginCallHttpClient& p = *reinterpret_cast<const _OrthancPluginCallHttpClient*>(parameters);
3095
3096 HttpClient client;
3097
3098 {
3099 _OrthancPluginCallHttpClient2 converted;
3100 memset(&converted, 0, sizeof(converted));
3101
3102 converted.answerBody = NULL;
3103 converted.answerHeaders = NULL;
3104 converted.httpStatus = NULL;
3105 converted.method = p.method;
3106 converted.url = p.url;
3107 converted.headersCount = 0;
3108 converted.headersKeys = NULL;
3109 converted.headersValues = NULL;
3110 converted.body = p.body;
3111 converted.bodySize = p.bodySize;
3112 converted.username = p.username;
3113 converted.password = p.password;
3114 converted.timeout = 0; // Use default timeout
3115 converted.certificateFile = NULL;
3116 converted.certificateKeyFile = NULL;
3117 converted.certificateKeyPassword = NULL;
3118 converted.pkcs11 = false;
3119
3120 SetupHttpClient(client, converted);
3121 }
3122
3123 uint16_t status;
3124 ExecuteHttpClientWithoutChunkedBody(status, p.target, NULL, client);
3125 }
3126
3127
3128 void OrthancPlugins::CallHttpClient2(const void* parameters)
3129 {
3130 const _OrthancPluginCallHttpClient2& p = *reinterpret_cast<const _OrthancPluginCallHttpClient2*>(parameters);
3131
3132 if (p.httpStatus == NULL)
3133 {
3134 throw OrthancException(ErrorCode_NullPointer);
3135 }
3136
3137 HttpClient client;
3138
3139 if (p.method == OrthancPluginHttpMethod_Post ||
3140 p.method == OrthancPluginHttpMethod_Put)
3141 {
3142 client.GetBody().assign(reinterpret_cast<const char*>(p.body), p.bodySize);
3143 }
3144
3145 SetupHttpClient(client, p);
3146 ExecuteHttpClientWithoutChunkedBody(*p.httpStatus, p.answerBody, p.answerHeaders, client);
3147 }
3148
3149
3150 void OrthancPlugins::ChunkedHttpClient(const void* parameters)
3151 {
3152 const _OrthancPluginChunkedHttpClient& p =
3153 *reinterpret_cast<const _OrthancPluginChunkedHttpClient*>(parameters);
3154
3155 if (p.httpStatus == NULL)
3156 {
3157 throw OrthancException(ErrorCode_NullPointer);
3158 }
3159
3160 HttpClient client;
3161
3162 {
3163 _OrthancPluginCallHttpClient2 converted;
3164 memset(&converted, 0, sizeof(converted));
3165
3166 converted.answerBody = NULL;
3167 converted.answerHeaders = NULL;
3168 converted.httpStatus = NULL;
3169 converted.method = p.method;
3170 converted.url = p.url;
3171 converted.headersCount = p.headersCount;
3172 converted.headersKeys = p.headersKeys;
3173 converted.headersValues = p.headersValues;
3174 converted.body = NULL;
3175 converted.bodySize = 0;
3176 converted.username = p.username;
3177 converted.password = p.password;
3178 converted.timeout = p.timeout;
3179 converted.certificateFile = p.certificateFile;
3180 converted.certificateKeyFile = p.certificateKeyFile;
3181 converted.certificateKeyPassword = p.certificateKeyPassword;
3182 converted.pkcs11 = p.pkcs11;
3183
3184 SetupHttpClient(client, converted);
3185 }
3186
3187 HttpClientChunkedRequest body(p, pimpl_->dictionary_);
3188 client.SetBody(body);
3189
3190 HttpClientChunkedAnswer answer(p, pimpl_->dictionary_);
3191
3192 bool success = client.Apply(answer);
3193
3194 *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus());
3195
3196 if (!success)
3197 {
3198 HttpClient::ThrowException(client.GetLastStatus());
3199 }
3200 }
3201
3202
3203 void OrthancPlugins::CallPeerApi(const void* parameters)
3204 {
3205 const _OrthancPluginCallPeerApi& p = *reinterpret_cast<const _OrthancPluginCallPeerApi*>(parameters);
3206 const OrthancPeers& peers = *reinterpret_cast<const OrthancPeers*>(p.peers);
3207
3208 HttpClient client(peers.GetPeerParameters(p.peerIndex), p.uri);
3209 client.SetConvertHeadersToLowerCase(false);
3210
3211 if (p.timeout != 0)
3212 {
3213 client.SetTimeout(p.timeout);
3214 }
3215
3216 for (uint32_t i = 0; i < p.additionalHeadersCount; i++)
3217 {
3218 if (p.additionalHeadersKeys[i] == NULL ||
3219 p.additionalHeadersValues[i] == NULL)
3220 {
3221 throw OrthancException(ErrorCode_NullPointer);
3222 }
3223
3224 client.AddHeader(p.additionalHeadersKeys[i], p.additionalHeadersValues[i]);
3225 }
3226
3227 switch (p.method)
3228 {
3229 case OrthancPluginHttpMethod_Get:
3230 client.SetMethod(HttpMethod_Get);
3231 break;
3232
3233 case OrthancPluginHttpMethod_Post:
3234 client.SetMethod(HttpMethod_Post);
3235 client.GetBody().assign(reinterpret_cast<const char*>(p.body), p.bodySize);
3236 break;
3237
3238 case OrthancPluginHttpMethod_Put:
3239 client.SetMethod(HttpMethod_Put);
3240 client.GetBody().assign(reinterpret_cast<const char*>(p.body), p.bodySize);
3241 break;
3242
3243 case OrthancPluginHttpMethod_Delete:
3244 client.SetMethod(HttpMethod_Delete);
3245 break;
3246
3247 default:
3248 throw OrthancException(ErrorCode_ParameterOutOfRange);
3249 }
3250
3251 std::string body;
3252 HttpClient::HttpHeaders headers;
3253
3254 bool success = client.Apply(body, headers);
3255
3256 // The HTTP request has succeeded
3257 *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus());
3258
3259 if (!success)
3260 {
3261 HttpClient::ThrowException(client.GetLastStatus());
3262 }
3263
3264 // Copy the HTTP headers of the answer, if the plugin requested them
3265 if (p.answerHeaders != NULL)
3266 {
3267 Json::Value json = Json::objectValue;
3268
3269 for (HttpClient::HttpHeaders::const_iterator
3270 it = headers.begin(); it != headers.end(); ++it)
3271 {
3272 json[it->first] = it->second;
3273 }
3274
3275 std::string s = json.toStyledString();
3276 CopyToMemoryBuffer(*p.answerHeaders, s);
3277 }
3278
3279 // Copy the body of the answer if it makes sense
3280 if (p.method != OrthancPluginHttpMethod_Delete)
3281 {
3282 CopyToMemoryBuffer(*p.answerBody, body);
3283 }
3284 }
3285
3286
3287 void OrthancPlugins::ConvertPixelFormat(const void* parameters)
3288 {
3289 const _OrthancPluginConvertPixelFormat& p = *reinterpret_cast<const _OrthancPluginConvertPixelFormat*>(parameters);
3290 const ImageAccessor& source = *reinterpret_cast<const ImageAccessor*>(p.source);
3291
3292 std::unique_ptr<ImageAccessor> target(new Image(Plugins::Convert(p.targetFormat), source.GetWidth(), source.GetHeight(), false));
3293 ImageProcessing::Convert(*target, source);
3294
3295 *(p.target) = ReturnImage(target);
3296 }
3297
3298
3299
3300 void OrthancPlugins::GetFontInfo(const void* parameters)
3301 {
3302 const _OrthancPluginGetFontInfo& p = *reinterpret_cast<const _OrthancPluginGetFontInfo*>(parameters);
3303
3304 {
3305 OrthancConfiguration::ReaderLock lock;
3306
3307 const Font& font = lock.GetConfiguration().GetFontRegistry().GetFont(p.fontIndex);
3308
3309 if (p.name != NULL)
3310 {
3311 *(p.name) = font.GetName().c_str();
3312 }
3313 else if (p.size != NULL)
3314 {
3315 *(p.size) = font.GetSize();
3316 }
3317 else
3318 {
3319 throw OrthancException(ErrorCode_InternalError);
3320 }
3321 }
3322 }
3323
3324
3325 void OrthancPlugins::DrawText(const void* parameters)
3326 {
3327 const _OrthancPluginDrawText& p = *reinterpret_cast<const _OrthancPluginDrawText*>(parameters);
3328
3329 ImageAccessor& target = *reinterpret_cast<ImageAccessor*>(p.image);
3330
3331 {
3332 OrthancConfiguration::ReaderLock lock;
3333 const Font& font = lock.GetConfiguration().GetFontRegistry().GetFont(p.fontIndex);
3334 font.Draw(target, p.utf8Text, p.x, p.y, p.r, p.g, p.b);
3335 }
3336 }
3337
3338
3339 void OrthancPlugins::ApplyDicomToJson(_OrthancPluginService service,
3340 const void* parameters)
3341 {
3342 const _OrthancPluginDicomToJson& p =
3343 *reinterpret_cast<const _OrthancPluginDicomToJson*>(parameters);
3344
3345 std::unique_ptr<ParsedDicomFile> dicom;
3346
3347 if (service == _OrthancPluginService_DicomBufferToJson)
3348 {
3349 dicom.reset(new ParsedDicomFile(p.buffer, p.size));
3350 }
3351 else
3352 {
3353 if (p.instanceId == NULL)
3354 {
3355 throw OrthancException(ErrorCode_NullPointer);
3356 }
3357
3358 std::string content;
3359
3360 {
3361 PImpl::ServerContextLock lock(*pimpl_);
3362 lock.GetContext().ReadDicom(content, p.instanceId);
3363 }
3364
3365 dicom.reset(new ParsedDicomFile(content));
3366 }
3367
3368 Json::Value json;
3369 dicom->DatasetToJson(json, Plugins::Convert(p.format),
3370 static_cast<DicomToJsonFlags>(p.flags), p.maxStringLength);
3371
3372 Json::FastWriter writer;
3373 *p.result = CopyString(writer.write(json));
3374 }
3375
3376
3377 void OrthancPlugins::ApplyCreateDicom(_OrthancPluginService service,
3378 const void* parameters)
3379 {
3380 const _OrthancPluginCreateDicom& p =
3381 *reinterpret_cast<const _OrthancPluginCreateDicom*>(parameters);
3382
3383 Json::Value json;
3384
3385 if (p.json == NULL)
3386 {
3387 json = Json::objectValue;
3388 }
3389 else
3390 {
3391 Json::Reader reader;
3392 if (!reader.parse(p.json, json))
3393 {
3394 throw OrthancException(ErrorCode_BadJson);
3395 }
3396 }
3397
3398 std::string dicom;
3399
3400 {
3401 // Fix issue 168 (Plugins can't read private tags from the
3402 // configuration file)
3403 // https://bitbucket.org/sjodogne/orthanc/issues/168/
3404 std::string privateCreator;
3405 {
3406 OrthancConfiguration::ReaderLock lock;
3407 privateCreator = lock.GetConfiguration().GetDefaultPrivateCreator();
3408 }
3409
3410 std::unique_ptr<ParsedDicomFile> file
3411 (ParsedDicomFile::CreateFromJson(json, static_cast<DicomFromJsonFlags>(p.flags),
3412 privateCreator));
3413
3414 if (p.pixelData)
3415 {
3416 file->EmbedImage(*reinterpret_cast<const ImageAccessor*>(p.pixelData));
3417 }
3418
3419 file->SaveToMemoryBuffer(dicom);
3420 }
3421
3422 CopyToMemoryBuffer(*p.target, dicom);
3423 }
3424
3425
3426 void OrthancPlugins::ComputeHash(_OrthancPluginService service,
3427 const void* parameters)
3428 {
3429 const _OrthancPluginComputeHash& p =
3430 *reinterpret_cast<const _OrthancPluginComputeHash*>(parameters);
3431
3432 std::string hash;
3433 switch (service)
3434 {
3435 case _OrthancPluginService_ComputeMd5:
3436 Toolbox::ComputeMD5(hash, p.buffer, p.size);
3437 break;
3438
3439 case _OrthancPluginService_ComputeSha1:
3440 Toolbox::ComputeSHA1(hash, p.buffer, p.size);
3441 break;
3442
3443 default:
3444 throw OrthancException(ErrorCode_ParameterOutOfRange);
3445 }
3446
3447 *p.result = CopyString(hash);
3448 }
3449
3450
3451 void OrthancPlugins::GetTagName(const void* parameters)
3452 {
3453 const _OrthancPluginGetTagName& p =
3454 *reinterpret_cast<const _OrthancPluginGetTagName*>(parameters);
3455
3456 std::string privateCreator;
3457
3458 if (p.privateCreator != NULL)
3459 {
3460 privateCreator = p.privateCreator;
3461 }
3462
3463 DicomTag tag(p.group, p.element);
3464 *p.result = CopyString(FromDcmtkBridge::GetTagName(tag, privateCreator));
3465 }
3466
3467
3468 void OrthancPlugins::ApplyCreateImage(_OrthancPluginService service,
3469 const void* parameters)
3470 {
3471 const _OrthancPluginCreateImage& p =
3472 *reinterpret_cast<const _OrthancPluginCreateImage*>(parameters);
3473
3474 std::unique_ptr<ImageAccessor> result;
3475
3476 switch (service)
3477 {
3478 case _OrthancPluginService_CreateImage:
3479 result.reset(new Image(Plugins::Convert(p.format), p.width, p.height, false));
3480 break;
3481
3482 case _OrthancPluginService_CreateImageAccessor:
3483 result.reset(new ImageAccessor);
3484 result->AssignWritable(Plugins::Convert(p.format), p.width, p.height, p.pitch, p.buffer);
3485 break;
3486
3487 case _OrthancPluginService_DecodeDicomImage:
3488 {
3489 PImpl::ServerContextLock lock(*pimpl_);
3490 result.reset(lock.GetContext().DecodeDicomFrame(p.constBuffer, p.bufferSize, p.frameIndex));
3491 break;
3492 }
3493
3494 default:
3495 throw OrthancException(ErrorCode_InternalError);
3496 }
3497
3498 *(p.target) = ReturnImage(result);
3499 }
3500
3501
3502 void OrthancPlugins::ApplySendMultipartItem(const void* parameters)
3503 {
3504 // An exception might be raised in this function if the
3505 // connection was closed by the HTTP client.
3506 const _OrthancPluginAnswerBuffer& p =
3507 *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters);
3508
3509 std::map<std::string, std::string> headers; // No custom headers
3510 reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->SendMultipartItem(p.answer, p.answerSize, headers);
3511 }
3512
3513
3514 void OrthancPlugins::ApplySendMultipartItem2(const void* parameters)
3515 {
3516 // An exception might be raised in this function if the
3517 // connection was closed by the HTTP client.
3518 const _OrthancPluginSendMultipartItem2& p =
3519 *reinterpret_cast<const _OrthancPluginSendMultipartItem2*>(parameters);
3520
3521 std::map<std::string, std::string> headers;
3522 for (uint32_t i = 0; i < p.headersCount; i++)
3523 {
3524 headers[p.headersKeys[i]] = p.headersValues[i];
3525 }
3526
3527 reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->SendMultipartItem(p.answer, p.answerSize, headers);
3528 }
3529
3530
3531 void OrthancPlugins::DatabaseAnswer(const void* parameters)
3532 {
3533 const _OrthancPluginDatabaseAnswer& p =
3534 *reinterpret_cast<const _OrthancPluginDatabaseAnswer*>(parameters);
3535
3536 if (pimpl_->database_.get() != NULL)
3537 {
3538 pimpl_->database_->AnswerReceived(p);
3539 }
3540 else
3541 {
3542 throw OrthancException(ErrorCode_BadRequest,
3543 "Cannot invoke this service without a custom database back-end");
3544 }
3545 }
3546
3547
3548 namespace
3549 {
3550 class DictionaryReadLocker
3551 {
3552 private:
3553 const DcmDataDictionary& dictionary_;
3554
3555 public:
3556 DictionaryReadLocker() : dictionary_(dcmDataDict.rdlock())
3557 {
3558 }
3559
3560 ~DictionaryReadLocker()
3561 {
3562 #if DCMTK_VERSION_NUMBER >= 364
3563 dcmDataDict.rdunlock();
3564 #else
3565 dcmDataDict.unlock();
3566 #endif
3567 }
3568
3569 const DcmDataDictionary* operator->()
3570 {
3571 return &dictionary_;
3572 }
3573 };
3574 }
3575
3576
3577 void OrthancPlugins::ApplyLookupDictionary(const void* parameters)
3578 {
3579 const _OrthancPluginLookupDictionary& p =
3580 *reinterpret_cast<const _OrthancPluginLookupDictionary*>(parameters);
3581
3582 DicomTag tag(FromDcmtkBridge::ParseTag(p.name));
3583 DcmTagKey tag2(tag.GetGroup(), tag.GetElement());
3584
3585 DictionaryReadLocker locker;
3586 const DcmDictEntry* entry = NULL;
3587
3588 if (tag.IsPrivate())
3589 {
3590 // Fix issue 168 (Plugins can't read private tags from the
3591 // configuration file)
3592 // https://bitbucket.org/sjodogne/orthanc/issues/168/
3593 std::string privateCreator;
3594 {
3595 OrthancConfiguration::ReaderLock lock;
3596 privateCreator = lock.GetConfiguration().GetDefaultPrivateCreator();
3597 }
3598
3599 entry = locker->findEntry(tag2, privateCreator.c_str());
3600 }
3601 else
3602 {
3603 entry = locker->findEntry(tag2, NULL);
3604 }
3605
3606 if (entry == NULL)
3607 {
3608 throw OrthancException(ErrorCode_UnknownDicomTag);
3609 }
3610 else
3611 {
3612 p.target->group = entry->getKey().getGroup();
3613 p.target->element = entry->getKey().getElement();
3614 p.target->vr = Plugins::Convert(FromDcmtkBridge::Convert(entry->getEVR()));
3615 p.target->minMultiplicity = static_cast<uint32_t>(entry->getVMMin());
3616 p.target->maxMultiplicity = (entry->getVMMax() == DcmVariableVM ? 0 : static_cast<uint32_t>(entry->getVMMax()));
3617 }
3618 }
3619
3620
3621 bool OrthancPlugins::InvokeSafeService(SharedLibrary& plugin,
3622 _OrthancPluginService service,
3623 const void* parameters)
3624 {
3625 // Services that can be run without mutual exclusion
3626
3627 switch (service)
3628 {
3629 case _OrthancPluginService_GetOrthancPath:
3630 {
3631 std::string s = SystemToolbox::GetPathToExecutable();
3632 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
3633 return true;
3634 }
3635
3636 case _OrthancPluginService_GetOrthancDirectory:
3637 {
3638 std::string s = SystemToolbox::GetDirectoryOfExecutable();
3639 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
3640 return true;
3641 }
3642
3643 case _OrthancPluginService_GetConfigurationPath:
3644 {
3645 std::string s;
3646
3647 {
3648 OrthancConfiguration::ReaderLock lock;
3649 s = lock.GetConfiguration().GetConfigurationAbsolutePath();
3650 }
3651
3652 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
3653 return true;
3654 }
3655
3656 case _OrthancPluginService_GetConfiguration:
3657 {
3658 std::string s;
3659
3660 {
3661 OrthancConfiguration::ReaderLock lock;
3662 lock.GetConfiguration().Format(s);
3663 }
3664
3665 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
3666 return true;
3667 }
3668
3669 case _OrthancPluginService_BufferCompression:
3670 BufferCompression(parameters);
3671 return true;
3672
3673 case _OrthancPluginService_AnswerBuffer:
3674 AnswerBuffer(parameters);
3675 return true;
3676
3677 case _OrthancPluginService_CompressAndAnswerPngImage:
3678 CompressAndAnswerPngImage(parameters);
3679 return true;
3680
3681 case _OrthancPluginService_CompressAndAnswerImage:
3682 CompressAndAnswerImage(parameters);
3683 return true;
3684
3685 case _OrthancPluginService_GetDicomForInstance:
3686 GetDicomForInstance(parameters);
3687 return true;
3688
3689 case _OrthancPluginService_RestApiGet:
3690 RestApiGet(parameters, false);
3691 return true;
3692
3693 case _OrthancPluginService_RestApiGetAfterPlugins:
3694 RestApiGet(parameters, true);
3695 return true;
3696
3697 case _OrthancPluginService_RestApiGet2:
3698 RestApiGet2(parameters);
3699 return true;
3700
3701 case _OrthancPluginService_RestApiPost:
3702 RestApiPostPut(true, parameters, false);
3703 return true;
3704
3705 case _OrthancPluginService_RestApiPostAfterPlugins:
3706 RestApiPostPut(true, parameters, true);
3707 return true;
3708
3709 case _OrthancPluginService_RestApiDelete:
3710 RestApiDelete(parameters, false);
3711 return true;
3712
3713 case _OrthancPluginService_RestApiDeleteAfterPlugins:
3714 RestApiDelete(parameters, true);
3715 return true;
3716
3717 case _OrthancPluginService_RestApiPut:
3718 RestApiPostPut(false, parameters, false);
3719 return true;
3720
3721 case _OrthancPluginService_RestApiPutAfterPlugins:
3722 RestApiPostPut(false, parameters, true);
3723 return true;
3724
3725 case _OrthancPluginService_Redirect:
3726 Redirect(parameters);
3727 return true;
3728
3729 case _OrthancPluginService_SendUnauthorized:
3730 SendUnauthorized(parameters);
3731 return true;
3732
3733 case _OrthancPluginService_SendMethodNotAllowed:
3734 SendMethodNotAllowed(parameters);
3735 return true;
3736
3737 case _OrthancPluginService_SendHttpStatus:
3738 SendHttpStatus(parameters);
3739 return true;
3740
3741 case _OrthancPluginService_SendHttpStatusCode:
3742 SendHttpStatusCode(parameters);
3743 return true;
3744
3745 case _OrthancPluginService_SetCookie:
3746 SetCookie(parameters);
3747 return true;
3748
3749 case _OrthancPluginService_SetHttpHeader:
3750 SetHttpHeader(parameters);
3751 return true;
3752
3753 case _OrthancPluginService_SetHttpErrorDetails:
3754 SetHttpErrorDetails(parameters);
3755 return true;
3756
3757 case _OrthancPluginService_LookupPatient:
3758 case _OrthancPluginService_LookupStudy:
3759 case _OrthancPluginService_LookupStudyWithAccessionNumber:
3760 case _OrthancPluginService_LookupSeries:
3761 case _OrthancPluginService_LookupInstance:
3762 LookupResource(service, parameters);
3763 return true;
3764
3765 case _OrthancPluginService_GetInstanceRemoteAet:
3766 case _OrthancPluginService_GetInstanceSize:
3767 case _OrthancPluginService_GetInstanceData:
3768 case _OrthancPluginService_GetInstanceJson:
3769 case _OrthancPluginService_GetInstanceSimplifiedJson:
3770 case _OrthancPluginService_HasInstanceMetadata:
3771 case _OrthancPluginService_GetInstanceMetadata:
3772 case _OrthancPluginService_GetInstanceOrigin:
3773 case _OrthancPluginService_GetInstanceTransferSyntaxUid:
3774 case _OrthancPluginService_HasInstancePixelData:
3775 AccessDicomInstance(service, parameters);
3776 return true;
3777
3778 case _OrthancPluginService_GetInstanceFramesCount:
3779 case _OrthancPluginService_GetInstanceRawFrame:
3780 case _OrthancPluginService_GetInstanceDecodedFrame:
3781 case _OrthancPluginService_SerializeDicomInstance:
3782 case _OrthancPluginService_GetInstanceAdvancedJson:
3783 case _OrthancPluginService_GetInstanceDicomWebJson:
3784 case _OrthancPluginService_GetInstanceDicomWebXml:
3785 AccessDicomInstance2(service, parameters);
3786 return true;
3787
3788 case _OrthancPluginService_SetGlobalProperty:
3789 {
3790 const _OrthancPluginGlobalProperty& p =
3791 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
3792 if (p.property < 1024)
3793 {
3794 return false;
3795 }
3796 else
3797 {
3798 PImpl::ServerContextLock lock(*pimpl_);
3799 lock.GetContext().GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
3800 return true;
3801 }
3802 }
3803
3804 case _OrthancPluginService_GetGlobalProperty:
3805 {
3806 const _OrthancPluginGlobalProperty& p =
3807 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
3808
3809 std::string result;
3810
3811 {
3812 PImpl::ServerContextLock lock(*pimpl_);
3813 result = lock.GetContext().GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property), p.value);
3814 }
3815
3816 *(p.result) = CopyString(result);
3817 return true;
3818 }
3819
3820 case _OrthancPluginService_GetExpectedDatabaseVersion:
3821 {
3822 const _OrthancPluginReturnSingleValue& p =
3823 *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
3824 *(p.resultUint32) = ORTHANC_DATABASE_VERSION;
3825 return true;
3826 }
3827
3828 case _OrthancPluginService_StartMultipartAnswer:
3829 {
3830 const _OrthancPluginStartMultipartAnswer& p =
3831 *reinterpret_cast<const _OrthancPluginStartMultipartAnswer*>(parameters);
3832 reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->StartMultipart(p.subType, p.contentType);
3833 return true;
3834 }
3835
3836 case _OrthancPluginService_SendMultipartItem:
3837 ApplySendMultipartItem(parameters);
3838 return true;
3839
3840 case _OrthancPluginService_SendMultipartItem2:
3841 ApplySendMultipartItem2(parameters);
3842 return true;
3843
3844 case _OrthancPluginService_ReadFile:
3845 {
3846 const _OrthancPluginReadFile& p =
3847 *reinterpret_cast<const _OrthancPluginReadFile*>(parameters);
3848
3849 std::string content;
3850 SystemToolbox::ReadFile(content, p.path);
3851 CopyToMemoryBuffer(*p.target, content.size() > 0 ? content.c_str() : NULL, content.size());
3852
3853 return true;
3854 }
3855
3856 case _OrthancPluginService_WriteFile:
3857 {
3858 const _OrthancPluginWriteFile& p =
3859 *reinterpret_cast<const _OrthancPluginWriteFile*>(parameters);
3860 SystemToolbox::WriteFile(p.data, p.size, p.path);
3861 return true;
3862 }
3863
3864 case _OrthancPluginService_GetErrorDescription:
3865 {
3866 const _OrthancPluginGetErrorDescription& p =
3867 *reinterpret_cast<const _OrthancPluginGetErrorDescription*>(parameters);
3868 *(p.target) = EnumerationToString(static_cast<ErrorCode>(p.error));
3869 return true;
3870 }
3871
3872 case _OrthancPluginService_GetImagePixelFormat:
3873 {
3874 const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
3875 *(p.resultPixelFormat) = Plugins::Convert(reinterpret_cast<const ImageAccessor*>(p.image)->GetFormat());
3876 return true;
3877 }
3878
3879 case _OrthancPluginService_GetImageWidth:
3880 {
3881 const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
3882 *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetWidth();
3883 return true;
3884 }
3885
3886 case _OrthancPluginService_GetImageHeight:
3887 {
3888 const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
3889 *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetHeight();
3890 return true;
3891 }
3892
3893 case _OrthancPluginService_GetImagePitch:
3894 {
3895 const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
3896 *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetPitch();
3897 return true;
3898 }
3899
3900 case _OrthancPluginService_GetImageBuffer:
3901 {
3902 const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
3903 *(p.resultBuffer) = reinterpret_cast<const ImageAccessor*>(p.image)->GetBuffer();
3904 return true;
3905 }
3906
3907 case _OrthancPluginService_FreeImage:
3908 {
3909 const _OrthancPluginFreeImage& p = *reinterpret_cast<const _OrthancPluginFreeImage*>(parameters);
3910
3911 if (p.image != NULL)
3912 {
3913 delete reinterpret_cast<ImageAccessor*>(p.image);
3914 }
3915
3916 return true;
3917 }
3918
3919 case _OrthancPluginService_UncompressImage:
3920 UncompressImage(parameters);
3921 return true;
3922
3923 case _OrthancPluginService_CompressImage:
3924 CompressImage(parameters);
3925 return true;
3926
3927 case _OrthancPluginService_CallHttpClient:
3928 CallHttpClient(parameters);
3929 return true;
3930
3931 case _OrthancPluginService_CallHttpClient2:
3932 CallHttpClient2(parameters);
3933 return true;
3934
3935 case _OrthancPluginService_ChunkedHttpClient:
3936 ChunkedHttpClient(parameters);
3937 return true;
3938
3939 case _OrthancPluginService_ConvertPixelFormat:
3940 ConvertPixelFormat(parameters);
3941 return true;
3942
3943 case _OrthancPluginService_GetFontsCount:
3944 {
3945 const _OrthancPluginReturnSingleValue& p =
3946 *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
3947
3948 {
3949 OrthancConfiguration::ReaderLock lock;
3950 *(p.resultUint32) = lock.GetConfiguration().GetFontRegistry().GetSize();
3951 }
3952
3953 return true;
3954 }
3955
3956 case _OrthancPluginService_GetFontInfo:
3957 GetFontInfo(parameters);
3958 return true;
3959
3960 case _OrthancPluginService_DrawText:
3961 DrawText(parameters);
3962 return true;
3963
3964 case _OrthancPluginService_StorageAreaCreate:
3965 {
3966 const _OrthancPluginStorageAreaCreate& p =
3967 *reinterpret_cast<const _OrthancPluginStorageAreaCreate*>(parameters);
3968 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
3969 storage.Create(p.uuid, p.content, static_cast<size_t>(p.size), Plugins::Convert(p.type));
3970 return true;
3971 }
3972
3973 case _OrthancPluginService_StorageAreaRead:
3974 {
3975 const _OrthancPluginStorageAreaRead& p =
3976 *reinterpret_cast<const _OrthancPluginStorageAreaRead*>(parameters);
3977 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
3978 std::string content;
3979 storage.Read(content, p.uuid, Plugins::Convert(p.type));
3980 CopyToMemoryBuffer(*p.target, content);
3981 return true;
3982 }
3983
3984 case _OrthancPluginService_StorageAreaRemove:
3985 {
3986 const _OrthancPluginStorageAreaRemove& p =
3987 *reinterpret_cast<const _OrthancPluginStorageAreaRemove*>(parameters);
3988 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
3989 storage.Remove(p.uuid, Plugins::Convert(p.type));
3990 return true;
3991 }
3992
3993 case _OrthancPluginService_DicomBufferToJson:
3994 case _OrthancPluginService_DicomInstanceToJson:
3995 ApplyDicomToJson(service, parameters);
3996 return true;
3997
3998 case _OrthancPluginService_CreateDicom:
3999 ApplyCreateDicom(service, parameters);
4000 return true;
4001
4002 case _OrthancPluginService_WorklistAddAnswer:
4003 {
4004 const _OrthancPluginWorklistAnswersOperation& p =
4005 *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
4006 reinterpret_cast<const WorklistHandler*>(p.query)->AddAnswer(p.answers, p.dicom, p.size);
4007 return true;
4008 }
4009
4010 case _OrthancPluginService_WorklistMarkIncomplete:
4011 {
4012 const _OrthancPluginWorklistAnswersOperation& p =
4013 *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
4014 reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
4015 return true;
4016 }
4017
4018 case _OrthancPluginService_WorklistIsMatch:
4019 {
4020 const _OrthancPluginWorklistQueryOperation& p =
4021 *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
4022 *p.isMatch = reinterpret_cast<const WorklistHandler*>(p.query)->IsMatch(p.dicom, p.size);
4023 return true;
4024 }
4025
4026 case _OrthancPluginService_WorklistGetDicomQuery:
4027 {
4028 const _OrthancPluginWorklistQueryOperation& p =
4029 *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
4030 reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(*p.target);
4031 return true;
4032 }
4033
4034 case _OrthancPluginService_FindAddAnswer:
4035 {
4036 const _OrthancPluginFindOperation& p =
4037 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
4038 reinterpret_cast<DicomFindAnswers*>(p.answers)->Add(p.dicom, p.size);
4039 return true;
4040 }
4041
4042 case _OrthancPluginService_FindMarkIncomplete:
4043 {
4044 const _OrthancPluginFindOperation& p =
4045 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
4046 reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
4047 return true;
4048 }
4049
4050 case _OrthancPluginService_GetFindQuerySize:
4051 case _OrthancPluginService_GetFindQueryTag:
4052 case _OrthancPluginService_GetFindQueryTagName:
4053 case _OrthancPluginService_GetFindQueryValue:
4054 {
4055 const _OrthancPluginFindOperation& p =
4056 *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
4057 reinterpret_cast<const FindHandler*>(p.query)->Invoke(service, p);
4058 return true;
4059 }
4060
4061 case _OrthancPluginService_CreateImage:
4062 case _OrthancPluginService_CreateImageAccessor:
4063 case _OrthancPluginService_DecodeDicomImage:
4064 ApplyCreateImage(service, parameters);
4065 return true;
4066
4067 case _OrthancPluginService_ComputeMd5:
4068 case _OrthancPluginService_ComputeSha1:
4069 ComputeHash(service, parameters);
4070 return true;
4071
4072 case _OrthancPluginService_LookupDictionary:
4073 ApplyLookupDictionary(parameters);
4074 return true;
4075
4076 case _OrthancPluginService_GenerateUuid:
4077 {
4078 *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result =
4079 CopyString(Toolbox::GenerateUuid());
4080 return true;
4081 }
4082
4083 case _OrthancPluginService_CreateFindMatcher:
4084 {
4085 const _OrthancPluginCreateFindMatcher& p =
4086 *reinterpret_cast<const _OrthancPluginCreateFindMatcher*>(parameters);
4087 ParsedDicomFile query(p.query, p.size);
4088 *(p.target) = reinterpret_cast<OrthancPluginFindMatcher*>(new HierarchicalMatcher(query));
4089 return true;
4090 }
4091
4092 case _OrthancPluginService_FreeFindMatcher:
4093 {
4094 const _OrthancPluginFreeFindMatcher& p =
4095 *reinterpret_cast<const _OrthancPluginFreeFindMatcher*>(parameters);
4096
4097 if (p.matcher != NULL)
4098 {
4099 delete reinterpret_cast<HierarchicalMatcher*>(p.matcher);
4100 }
4101
4102 return true;
4103 }
4104
4105 case _OrthancPluginService_FindMatcherIsMatch:
4106 {
4107 const _OrthancPluginFindMatcherIsMatch& p =
4108 *reinterpret_cast<const _OrthancPluginFindMatcherIsMatch*>(parameters);
4109
4110 if (p.matcher == NULL)
4111 {
4112 throw OrthancException(ErrorCode_NullPointer);
4113 }
4114 else
4115 {
4116 ParsedDicomFile query(p.dicom, p.size);
4117 *p.isMatch = reinterpret_cast<const HierarchicalMatcher*>(p.matcher)->Match(query) ? 1 : 0;
4118 return true;
4119 }
4120 }
4121
4122 case _OrthancPluginService_GetPeers:
4123 {
4124 const _OrthancPluginGetPeers& p =
4125 *reinterpret_cast<const _OrthancPluginGetPeers*>(parameters);
4126 *(p.peers) = reinterpret_cast<OrthancPluginPeers*>(new OrthancPeers);
4127 return true;
4128 }
4129
4130 case _OrthancPluginService_FreePeers:
4131 {
4132 const _OrthancPluginFreePeers& p =
4133 *reinterpret_cast<const _OrthancPluginFreePeers*>(parameters);
4134
4135 if (p.peers != NULL)
4136 {
4137 delete reinterpret_cast<OrthancPeers*>(p.peers);
4138 }
4139
4140 return true;
4141 }
4142
4143 case _OrthancPluginService_GetPeersCount:
4144 {
4145 const _OrthancPluginGetPeersCount& p =
4146 *reinterpret_cast<const _OrthancPluginGetPeersCount*>(parameters);
4147
4148 if (p.peers == NULL)
4149 {
4150 throw OrthancException(ErrorCode_NullPointer);
4151 }
4152 else
4153 {
4154 *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeersCount();
4155 return true;
4156 }
4157 }
4158
4159 case _OrthancPluginService_GetPeerName:
4160 {
4161 const _OrthancPluginGetPeerProperty& p =
4162 *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters);
4163
4164 if (p.peers == NULL)
4165 {
4166 throw OrthancException(ErrorCode_NullPointer);
4167 }
4168 else
4169 {
4170 *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerName(p.peerIndex).c_str();
4171 return true;
4172 }
4173 }
4174
4175 case _OrthancPluginService_GetPeerUrl:
4176 {
4177 const _OrthancPluginGetPeerProperty& p =
4178 *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters);
4179
4180 if (p.peers == NULL)
4181 {
4182 throw OrthancException(ErrorCode_NullPointer);
4183 }
4184 else
4185 {
4186 *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerParameters(p.peerIndex).GetUrl().c_str();
4187 return true;
4188 }
4189 }
4190
4191 case _OrthancPluginService_GetPeerUserProperty:
4192 {
4193 const _OrthancPluginGetPeerProperty& p =
4194 *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters);
4195
4196 if (p.peers == NULL ||
4197 p.userProperty == NULL)
4198 {
4199 throw OrthancException(ErrorCode_NullPointer);
4200 }
4201 else
4202 {
4203 const WebServiceParameters::Dictionary& properties =
4204 reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerParameters(p.peerIndex).GetUserProperties();
4205
4206 WebServiceParameters::Dictionary::const_iterator found =
4207 properties.find(p.userProperty);
4208
4209 if (found == properties.end())
4210 {
4211 *(p.target) = NULL;
4212 }
4213 else
4214 {
4215 *(p.target) = found->second.c_str();
4216 }
4217
4218 return true;
4219 }
4220 }
4221
4222 case _OrthancPluginService_CallPeerApi:
4223 CallPeerApi(parameters);
4224 return true;
4225
4226 case _OrthancPluginService_CreateJob:
4227 {
4228 const _OrthancPluginCreateJob& p =
4229 *reinterpret_cast<const _OrthancPluginCreateJob*>(parameters);
4230 *(p.target) = reinterpret_cast<OrthancPluginJob*>(new PluginsJob(p));
4231 return true;
4232 }
4233
4234 case _OrthancPluginService_FreeJob:
4235 {
4236 const _OrthancPluginFreeJob& p =
4237 *reinterpret_cast<const _OrthancPluginFreeJob*>(parameters);
4238
4239 if (p.job != NULL)
4240 {
4241 delete reinterpret_cast<PluginsJob*>(p.job);
4242 }
4243
4244 return true;
4245 }
4246
4247 case _OrthancPluginService_SubmitJob:
4248 {
4249 const _OrthancPluginSubmitJob& p =
4250 *reinterpret_cast<const _OrthancPluginSubmitJob*>(parameters);
4251
4252 std::string uuid;
4253
4254 PImpl::ServerContextLock lock(*pimpl_);
4255 lock.GetContext().GetJobsEngine().GetRegistry().Submit
4256 (uuid, reinterpret_cast<PluginsJob*>(p.job), p.priority);
4257
4258 *p.resultId = CopyString(uuid);
4259
4260 return true;
4261 }
4262
4263 case _OrthancPluginService_AutodetectMimeType:
4264 {
4265 const _OrthancPluginRetrieveStaticString& p =
4266 *reinterpret_cast<const _OrthancPluginRetrieveStaticString*>(parameters);
4267 *p.result = EnumerationToString(SystemToolbox::AutodetectMimeType(p.argument));
4268 return true;
4269 }
4270
4271 case _OrthancPluginService_SetMetricsValue:
4272 {
4273 const _OrthancPluginSetMetricsValue& p =
4274 *reinterpret_cast<const _OrthancPluginSetMetricsValue*>(parameters);
4275
4276 MetricsType type;
4277 switch (p.type)
4278 {
4279 case OrthancPluginMetricsType_Default:
4280 type = MetricsType_Default;
4281 break;
4282
4283 case OrthancPluginMetricsType_Timer:
4284 type = MetricsType_MaxOver10Seconds;
4285 break;
4286
4287 default:
4288 throw OrthancException(ErrorCode_ParameterOutOfRange);
4289 }
4290
4291 {
4292 PImpl::ServerContextLock lock(*pimpl_);
4293 lock.GetContext().GetMetricsRegistry().SetValue(p.name, p.value, type);
4294 }
4295
4296 return true;
4297 }
4298
4299 case _OrthancPluginService_EncodeDicomWebJson:
4300 case _OrthancPluginService_EncodeDicomWebXml:
4301 {
4302 const _OrthancPluginEncodeDicomWeb& p =
4303 *reinterpret_cast<const _OrthancPluginEncodeDicomWeb*>(parameters);
4304
4305 DicomWebBinaryFormatter formatter(p.callback);
4306 formatter.Apply(p.target,
4307 (service == _OrthancPluginService_EncodeDicomWebJson),
4308 p.dicom, p.dicomSize);
4309 return true;
4310 }
4311
4312 case _OrthancPluginService_EncodeDicomWebJson2:
4313 case _OrthancPluginService_EncodeDicomWebXml2:
4314 {
4315 const _OrthancPluginEncodeDicomWeb2& p =
4316 *reinterpret_cast<const _OrthancPluginEncodeDicomWeb2*>(parameters);
4317
4318 DicomWebBinaryFormatter formatter(p.callback, p.payload);
4319 formatter.Apply(p.target,
4320 (service == _OrthancPluginService_EncodeDicomWebJson2),
4321 p.dicom, p.dicomSize);
4322 return true;
4323 }
4324
4325 case _OrthancPluginService_GetTagName:
4326 GetTagName(parameters);
4327 return true;
4328
4329 case _OrthancPluginService_CreateDicomInstance:
4330 {
4331 const _OrthancPluginCreateDicomInstance& p =
4332 *reinterpret_cast<const _OrthancPluginCreateDicomInstance*>(parameters);
4333 *(p.target) = reinterpret_cast<OrthancPluginDicomInstance*>(
4334 new DicomInstanceFromBuffer(p.buffer, p.size));
4335 return true;
4336 }
4337
4338 case _OrthancPluginService_FreeDicomInstance:
4339 {
4340 const _OrthancPluginFreeDicomInstance& p =
4341 *reinterpret_cast<const _OrthancPluginFreeDicomInstance*>(parameters);
4342
4343 if (p.dicom != NULL)
4344 {
4345 IDicomInstance* obj = reinterpret_cast<IDicomInstance*>(p.dicom);
4346
4347 if (obj->CanBeFreed())
4348 {
4349 delete obj;
4350 }
4351 else
4352 {
4353 throw OrthancException(ErrorCode_Plugin, "Cannot free a DICOM instance provided to a callback");
4354 }
4355 }
4356
4357 return true;
4358 }
4359
4360 case _OrthancPluginService_TranscodeDicomInstance:
4361 {
4362 const _OrthancPluginCreateDicomInstance& p =
4363 *reinterpret_cast<const _OrthancPluginCreateDicomInstance*>(parameters);
4364
4365 DicomTransferSyntax transferSyntax;
4366 if (p.transferSyntax == NULL ||
4367 !LookupTransferSyntax(transferSyntax, p.transferSyntax))
4368 {
4369 throw OrthancException(ErrorCode_ParameterOutOfRange, "Unsupported transfer syntax: " +
4370 std::string(p.transferSyntax == NULL ? "(null)" : p.transferSyntax));
4371 }
4372 else
4373 {
4374 std::set<DicomTransferSyntax> syntaxes;
4375 syntaxes.insert(transferSyntax);
4376
4377 IDicomTranscoder::DicomImage source;
4378 source.SetExternalBuffer(p.buffer, p.size);
4379
4380 IDicomTranscoder::DicomImage transcoded;
4381 bool success;
4382
4383 {
4384 PImpl::ServerContextLock lock(*pimpl_);
4385 success = lock.GetContext().Transcode(
4386 transcoded, source, syntaxes, true /* allow new sop */);
4387 }
4388
4389 if (success)
4390 {
4391 *(p.target) = reinterpret_cast<OrthancPluginDicomInstance*>(
4392 new DicomInstanceFromTranscoded(transcoded));
4393 return true;
4394 }
4395 else
4396 {
4397 throw OrthancException(ErrorCode_NotImplemented, "Cannot transcode image");
4398 }
4399 }
4400 }
4401
4402 case _OrthancPluginService_CreateMemoryBuffer:
4403 {
4404 const _OrthancPluginCreateMemoryBuffer& p =
4405 *reinterpret_cast<const _OrthancPluginCreateMemoryBuffer*>(parameters);
4406
4407 p.target->size = p.size;
4408
4409 if (p.size == 0)
4410 {
4411 p.target->data = NULL;
4412 }
4413 else
4414 {
4415 p.target->data = malloc(p.size);
4416 }
4417
4418 return true;
4419 }
4420
4421 default:
4422 return false;
4423 }
4424 }
4425
4426
4427
4428 bool OrthancPlugins::InvokeProtectedService(SharedLibrary& plugin,
4429 _OrthancPluginService service,
4430 const void* parameters)
4431 {
4432 // Services that must be run in mutual exclusion. Guideline:
4433 // Whenever "pimpl_" is directly accessed by the service, it
4434 // should be listed here.
4435
4436 switch (service)
4437 {
4438 case _OrthancPluginService_RegisterRestCallback:
4439 RegisterRestCallback(parameters, true);
4440 return true;
4441
4442 case _OrthancPluginService_RegisterRestCallbackNoLock:
4443 RegisterRestCallback(parameters, false);
4444 return true;
4445
4446 case _OrthancPluginService_RegisterChunkedRestCallback:
4447 RegisterChunkedRestCallback(parameters);
4448 return true;
4449
4450 case _OrthancPluginService_RegisterOnStoredInstanceCallback:
4451 RegisterOnStoredInstanceCallback(parameters);
4452 return true;
4453
4454 case _OrthancPluginService_RegisterOnChangeCallback:
4455 RegisterOnChangeCallback(parameters);
4456 return true;
4457
4458 case _OrthancPluginService_RegisterWorklistCallback:
4459 RegisterWorklistCallback(parameters);
4460 return true;
4461
4462 case _OrthancPluginService_RegisterFindCallback:
4463 RegisterFindCallback(parameters);
4464 return true;
4465
4466 case _OrthancPluginService_RegisterMoveCallback:
4467 RegisterMoveCallback(parameters);
4468 return true;
4469
4470 case _OrthancPluginService_RegisterDecodeImageCallback:
4471 RegisterDecodeImageCallback(parameters);
4472 return true;
4473
4474 case _OrthancPluginService_RegisterTranscoderCallback:
4475 RegisterTranscoderCallback(parameters);
4476 return true;
4477
4478 case _OrthancPluginService_RegisterJobsUnserializer:
4479 RegisterJobsUnserializer(parameters);
4480 return true;
4481
4482 case _OrthancPluginService_RegisterIncomingHttpRequestFilter:
4483 RegisterIncomingHttpRequestFilter(parameters);
4484 return true;
4485
4486 case _OrthancPluginService_RegisterIncomingHttpRequestFilter2:
4487 RegisterIncomingHttpRequestFilter2(parameters);
4488 return true;
4489
4490 case _OrthancPluginService_RegisterIncomingDicomInstanceFilter:
4491 RegisterIncomingDicomInstanceFilter(parameters);
4492 return true;
4493
4494 case _OrthancPluginService_RegisterRefreshMetricsCallback:
4495 RegisterRefreshMetricsCallback(parameters);
4496 return true;
4497
4498 case _OrthancPluginService_RegisterStorageCommitmentScpCallback:
4499 RegisterStorageCommitmentScpCallback(parameters);
4500 return true;
4501
4502 case _OrthancPluginService_RegisterStorageArea:
4503 {
4504 LOG(INFO) << "Plugin has registered a custom storage area";
4505 const _OrthancPluginRegisterStorageArea& p =
4506 *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
4507
4508 if (pimpl_->storageArea_.get() == NULL)
4509 {
4510 pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary()));
4511 }
4512 else
4513 {
4514 throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered);
4515 }
4516
4517 return true;
4518 }
4519
4520 case _OrthancPluginService_SetPluginProperty:
4521 {
4522 const _OrthancPluginSetPluginProperty& p =
4523 *reinterpret_cast<const _OrthancPluginSetPluginProperty*>(parameters);
4524 pimpl_->properties_[std::make_pair(p.plugin, p.property)] = p.value;
4525 return true;
4526 }
4527
4528 case _OrthancPluginService_GetCommandLineArgumentsCount:
4529 {
4530 const _OrthancPluginReturnSingleValue& p =
4531 *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
4532 *(p.resultUint32) = pimpl_->argc_ - 1;
4533 return true;
4534 }
4535
4536 case _OrthancPluginService_GetCommandLineArgument:
4537 {
4538 const _OrthancPluginGlobalProperty& p =
4539 *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
4540
4541 if (p.property + 1 > pimpl_->argc_)
4542 {
4543 return false;
4544 }
4545 else
4546 {
4547 std::string arg = std::string(pimpl_->argv_[p.property + 1]);
4548 *(p.result) = CopyString(arg);
4549 return true;
4550 }
4551 }
4552
4553 case _OrthancPluginService_RegisterDatabaseBackend:
4554 {
4555 LOG(INFO) << "Plugin has registered a custom database back-end";
4556
4557 const _OrthancPluginRegisterDatabaseBackend& p =
4558 *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
4559
4560 if (pimpl_->database_.get() == NULL)
4561 {
4562 pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
4563 *p.backend, NULL, 0, p.payload));
4564 }
4565 else
4566 {
4567 throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
4568 }
4569
4570 *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
4571
4572 return true;
4573 }
4574
4575 case _OrthancPluginService_RegisterDatabaseBackendV2:
4576 {
4577 LOG(INFO) << "Plugin has registered a custom database back-end";
4578
4579 const _OrthancPluginRegisterDatabaseBackendV2& p =
4580 *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters);
4581
4582 if (pimpl_->database_.get() == NULL)
4583 {
4584 pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
4585 *p.backend, p.extensions,
4586 p.extensionsSize, p.payload));
4587 }
4588 else
4589 {
4590 throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
4591 }
4592
4593 *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
4594
4595 return true;
4596 }
4597
4598 case _OrthancPluginService_DatabaseAnswer:
4599 throw OrthancException(ErrorCode_InternalError); // Implemented before locking (*)
4600
4601 case _OrthancPluginService_RegisterErrorCode:
4602 {
4603 const _OrthancPluginRegisterErrorCode& p =
4604 *reinterpret_cast<const _OrthancPluginRegisterErrorCode*>(parameters);
4605 *(p.target) = pimpl_->dictionary_.Register(plugin, p.code, p.httpStatus, p.message);
4606 return true;
4607 }
4608
4609 case _OrthancPluginService_RegisterDictionaryTag:
4610 {
4611 const _OrthancPluginRegisterDictionaryTag& p =
4612 *reinterpret_cast<const _OrthancPluginRegisterDictionaryTag*>(parameters);
4613 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element),
4614 Plugins::Convert(p.vr), p.name,
4615 p.minMultiplicity, p.maxMultiplicity, "");
4616 return true;
4617 }
4618
4619 case _OrthancPluginService_RegisterPrivateDictionaryTag:
4620 {
4621 const _OrthancPluginRegisterPrivateDictionaryTag& p =
4622 *reinterpret_cast<const _OrthancPluginRegisterPrivateDictionaryTag*>(parameters);
4623 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element),
4624 Plugins::Convert(p.vr), p.name,
4625 p.minMultiplicity, p.maxMultiplicity, p.privateCreator);
4626 return true;
4627 }
4628
4629 case _OrthancPluginService_ReconstructMainDicomTags:
4630 {
4631 const _OrthancPluginReconstructMainDicomTags& p =
4632 *reinterpret_cast<const _OrthancPluginReconstructMainDicomTags*>(parameters);
4633
4634 if (pimpl_->database_.get() == NULL)
4635 {
4636 throw OrthancException(ErrorCode_DatabasePlugin,
4637 "The service ReconstructMainDicomTags can only be invoked by custom database plugins");
4638 }
4639
4640 IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
4641 ServerToolbox::ReconstructMainDicomTags(*pimpl_->database_, storage, Plugins::Convert(p.level));
4642
4643 return true;
4644 }
4645
4646 default:
4647 {
4648 // This service is unknown to the Orthanc plugin engine
4649 return false;
4650 }
4651 }
4652 }
4653
4654
4655
4656 bool OrthancPlugins::InvokeService(SharedLibrary& plugin,
4657 _OrthancPluginService service,
4658 const void* parameters)
4659 {
4660 VLOG(1) << "Calling service " << service << " from plugin " << plugin.GetPath();
4661
4662 if (service == _OrthancPluginService_DatabaseAnswer)
4663 {
4664 // This case solves a deadlock at (*) reported by James Webster
4665 // on 2015-10-27 that was present in versions of Orthanc <=
4666 // 0.9.4 and related to database plugins implementing a custom
4667 // index. The problem was that locking the database is already
4668 // ensured by the "ServerIndex" class if the invoked service is
4669 // "DatabaseAnswer".
4670 DatabaseAnswer(parameters);
4671 return true;
4672 }
4673
4674 if (InvokeSafeService(plugin, service, parameters))
4675 {
4676 // The invoked service does not require locking
4677 return true;
4678 }
4679 else
4680 {
4681 // The invoked service requires locking
4682 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); // (*)
4683 return InvokeProtectedService(plugin, service, parameters);
4684 }
4685 }
4686
4687
4688 bool OrthancPlugins::HasStorageArea() const
4689 {
4690 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
4691 return pimpl_->storageArea_.get() != NULL;
4692 }
4693
4694 bool OrthancPlugins::HasDatabaseBackend() const
4695 {
4696 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
4697 return pimpl_->database_.get() != NULL;
4698 }
4699
4700
4701 IStorageArea* OrthancPlugins::CreateStorageArea()
4702 {
4703 if (!HasStorageArea())
4704 {
4705 throw OrthancException(ErrorCode_BadSequenceOfCalls);
4706 }
4707 else
4708 {
4709 return pimpl_->storageArea_->Create();
4710 }
4711 }
4712
4713
4714 const SharedLibrary& OrthancPlugins::GetStorageAreaLibrary() const
4715 {
4716 if (!HasStorageArea())
4717 {
4718 throw OrthancException(ErrorCode_BadSequenceOfCalls);
4719 }
4720 else
4721 {
4722 return pimpl_->storageArea_->GetSharedLibrary();
4723 }
4724 }
4725
4726
4727 IDatabaseWrapper& OrthancPlugins::GetDatabaseBackend()
4728 {
4729 if (!HasDatabaseBackend())
4730 {
4731 throw OrthancException(ErrorCode_BadSequenceOfCalls);
4732 }
4733 else
4734 {
4735 return *pimpl_->database_;
4736 }
4737 }
4738
4739
4740 const SharedLibrary& OrthancPlugins::GetDatabaseBackendLibrary() const
4741 {
4742 if (!HasDatabaseBackend())
4743 {
4744 throw OrthancException(ErrorCode_BadSequenceOfCalls);
4745 }
4746 else
4747 {
4748 return pimpl_->database_->GetSharedLibrary();
4749 }
4750 }
4751
4752
4753 const char* OrthancPlugins::GetProperty(const char* plugin,
4754 _OrthancPluginProperty property) const
4755 {
4756 PImpl::Property p = std::make_pair(plugin, property);
4757 PImpl::Properties::const_iterator it = pimpl_->properties_.find(p);
4758
4759 if (it == pimpl_->properties_.end())
4760 {
4761 return NULL;
4762 }
4763 else
4764 {
4765 return it->second.c_str();
4766 }
4767 }
4768
4769
4770 void OrthancPlugins::SetCommandLineArguments(int argc, char* argv[])
4771 {
4772 if (argc < 1 || argv == NULL)
4773 {
4774 throw OrthancException(ErrorCode_ParameterOutOfRange);
4775 }
4776
4777 pimpl_->argc_ = argc;
4778 pimpl_->argv_ = argv;
4779 }
4780
4781
4782 PluginsManager& OrthancPlugins::GetManager()
4783 {
4784 return pimpl_->manager_;
4785 }
4786
4787
4788 const PluginsManager& OrthancPlugins::GetManager() const
4789 {
4790 return pimpl_->manager_;
4791 }
4792
4793
4794 PluginsErrorDictionary& OrthancPlugins::GetErrorDictionary()
4795 {
4796 return pimpl_->dictionary_;
4797 }
4798
4799
4800 IWorklistRequestHandler* OrthancPlugins::ConstructWorklistRequestHandler()
4801 {
4802 if (HasWorklistHandler())
4803 {
4804 return new WorklistHandler(*this);
4805 }
4806 else
4807 {
4808 return NULL;
4809 }
4810 }
4811
4812
4813 bool OrthancPlugins::HasWorklistHandler()
4814 {
4815 boost::mutex::scoped_lock lock(pimpl_->worklistCallbackMutex_);
4816 return pimpl_->worklistCallback_ != NULL;
4817 }
4818
4819
4820 IFindRequestHandler* OrthancPlugins::ConstructFindRequestHandler()
4821 {
4822 if (HasFindHandler())
4823 {
4824 return new FindHandler(*this);
4825 }
4826 else
4827 {
4828 return NULL;
4829 }
4830 }
4831
4832
4833 bool OrthancPlugins::HasFindHandler()
4834 {
4835 boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_);
4836 return pimpl_->findCallback_ != NULL;
4837 }
4838
4839
4840 IMoveRequestHandler* OrthancPlugins::ConstructMoveRequestHandler()
4841 {
4842 if (HasMoveHandler())
4843 {
4844 return new MoveHandler(*this);
4845 }
4846 else
4847 {
4848 return NULL;
4849 }
4850 }
4851
4852
4853 bool OrthancPlugins::HasMoveHandler()
4854 {
4855 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
4856 return pimpl_->moveCallbacks_.callback != NULL;
4857 }
4858
4859
4860 bool OrthancPlugins::HasCustomImageDecoder()
4861 {
4862 boost::shared_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
4863 return !pimpl_->decodeImageCallbacks_.empty();
4864 }
4865
4866
4867 bool OrthancPlugins::HasCustomTranscoder()
4868 {
4869 boost::shared_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
4870 return !pimpl_->transcoderCallbacks_.empty();
4871 }
4872
4873
4874 ImageAccessor* OrthancPlugins::Decode(const void* dicom,
4875 size_t size,
4876 unsigned int frame)
4877 {
4878 boost::shared_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
4879
4880 for (PImpl::DecodeImageCallbacks::const_iterator
4881 decoder = pimpl_->decodeImageCallbacks_.begin();
4882 decoder != pimpl_->decodeImageCallbacks_.end(); ++decoder)
4883 {
4884 OrthancPluginImage* pluginImage = NULL;
4885 if ((*decoder) (&pluginImage, dicom, size, frame) == OrthancPluginErrorCode_Success &&
4886 pluginImage != NULL)
4887 {
4888 return reinterpret_cast<ImageAccessor*>(pluginImage);
4889 }
4890 }
4891
4892 return NULL;
4893 }
4894
4895
4896 bool OrthancPlugins::IsAllowed(HttpMethod method,
4897 const char* uri,
4898 const char* ip,
4899 const char* username,
4900 const IHttpHandler::Arguments& httpHeaders,
4901 const IHttpHandler::GetArguments& getArguments)
4902 {
4903 OrthancPluginHttpMethod cMethod = Plugins::Convert(method);
4904
4905 std::vector<const char*> httpKeys(httpHeaders.size());
4906 std::vector<const char*> httpValues(httpHeaders.size());
4907
4908 size_t pos = 0;
4909 for (IHttpHandler::Arguments::const_iterator
4910 it = httpHeaders.begin(); it != httpHeaders.end(); ++it, pos++)
4911 {
4912 httpKeys[pos] = it->first.c_str();
4913 httpValues[pos] = it->second.c_str();
4914 }
4915
4916 std::vector<const char*> getKeys(getArguments.size());
4917 std::vector<const char*> getValues(getArguments.size());
4918
4919 for (size_t i = 0; i < getArguments.size(); i++)
4920 {
4921 getKeys[i] = getArguments[i].first.c_str();
4922 getValues[i] = getArguments[i].second.c_str();
4923 }
4924
4925 {
4926 boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
4927
4928 // Improved callback with support for GET arguments, since Orthanc 1.3.0
4929 for (PImpl::IncomingHttpRequestFilters2::const_iterator
4930 filter = pimpl_->incomingHttpRequestFilters2_.begin();
4931 filter != pimpl_->incomingHttpRequestFilters2_.end(); ++filter)
4932 {
4933 int32_t allowed = (*filter) (cMethod, uri, ip,
4934 httpKeys.size(),
4935 httpKeys.empty() ? NULL : &httpKeys[0],
4936 httpValues.empty() ? NULL : &httpValues[0],
4937 getKeys.size(),
4938 getKeys.empty() ? NULL : &getKeys[0],
4939 getValues.empty() ? NULL : &getValues[0]);
4940
4941 if (allowed == 0)
4942 {
4943 return false;
4944 }
4945 else if (allowed != 1)
4946 {
4947 // The callback is only allowed to answer 0 or 1
4948 throw OrthancException(ErrorCode_Plugin);
4949 }
4950 }
4951
4952 for (PImpl::IncomingHttpRequestFilters::const_iterator
4953 filter = pimpl_->incomingHttpRequestFilters_.begin();
4954 filter != pimpl_->incomingHttpRequestFilters_.end(); ++filter)
4955 {
4956 int32_t allowed = (*filter) (cMethod, uri, ip, httpKeys.size(),
4957 httpKeys.empty() ? NULL : &httpKeys[0],
4958 httpValues.empty() ? NULL : &httpValues[0]);
4959
4960 if (allowed == 0)
4961 {
4962 return false;
4963 }
4964 else if (allowed != 1)
4965 {
4966 // The callback is only allowed to answer 0 or 1
4967 throw OrthancException(ErrorCode_Plugin);
4968 }
4969 }
4970 }
4971
4972 return true;
4973 }
4974
4975
4976 IJob* OrthancPlugins::UnserializeJob(const std::string& type,
4977 const Json::Value& value)
4978 {
4979 const std::string serialized = value.toStyledString();
4980
4981 boost::mutex::scoped_lock lock(pimpl_->jobsUnserializersMutex_);
4982
4983 for (PImpl::JobsUnserializers::iterator
4984 unserializer = pimpl_->jobsUnserializers_.begin();
4985 unserializer != pimpl_->jobsUnserializers_.end(); ++unserializer)
4986 {
4987 OrthancPluginJob* job = (*unserializer) (type.c_str(), serialized.c_str());
4988 if (job != NULL)
4989 {
4990 return reinterpret_cast<PluginsJob*>(job);
4991 }
4992 }
4993
4994 return NULL;
4995 }
4996
4997
4998 void OrthancPlugins::RefreshMetrics()
4999 {
5000 boost::mutex::scoped_lock lock(pimpl_->refreshMetricsMutex_);
5001
5002 for (PImpl::RefreshMetricsCallbacks::iterator
5003 it = pimpl_->refreshMetricsCallbacks_.begin();
5004 it != pimpl_->refreshMetricsCallbacks_.end(); ++it)
5005 {
5006 if (*it != NULL)
5007 {
5008 (*it) ();
5009 }
5010 }
5011 }
5012
5013
5014 class OrthancPlugins::HttpServerChunkedReader : public IHttpHandler::IChunkedRequestReader
5015 {
5016 private:
5017 OrthancPluginServerChunkedRequestReader* reader_;
5018 _OrthancPluginChunkedRestCallback parameters_;
5019 PluginsErrorDictionary& errorDictionary_;
5020
5021 public:
5022 HttpServerChunkedReader(OrthancPluginServerChunkedRequestReader* reader,
5023 const _OrthancPluginChunkedRestCallback& parameters,
5024 PluginsErrorDictionary& errorDictionary) :
5025 reader_(reader),
5026 parameters_(parameters),
5027 errorDictionary_(errorDictionary)
5028 {
5029 assert(reader_ != NULL);
5030 }
5031
5032 virtual ~HttpServerChunkedReader()
5033 {
5034 assert(reader_ != NULL);
5035 parameters_.finalize(reader_);
5036 }
5037
5038 virtual void AddBodyChunk(const void* data,
5039 size_t size)
5040 {
5041 if (static_cast<uint32_t>(size) != size)
5042 {
5043 throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
5044 }
5045
5046 assert(reader_ != NULL);
5047 parameters_.addChunk(reader_, data, size);
5048 }
5049
5050 virtual void Execute(HttpOutput& output)
5051 {
5052 assert(reader_ != NULL);
5053
5054 PImpl::PluginHttpOutput pluginOutput(output);
5055
5056 OrthancPluginErrorCode error = parameters_.execute(
5057 reader_, reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput));
5058
5059 pluginOutput.Close(error, errorDictionary_);
5060 }
5061 };
5062
5063
5064 bool OrthancPlugins::CreateChunkedRequestReader(std::unique_ptr<IChunkedRequestReader>& target,
5065 RequestOrigin origin,
5066 const char* remoteIp,
5067 const char* username,
5068 HttpMethod method,
5069 const UriComponents& uri,
5070 const Arguments& headers)
5071 {
5072 if (method != HttpMethod_Post &&
5073 method != HttpMethod_Put)
5074 {
5075 throw OrthancException(ErrorCode_InternalError);
5076 }
5077
5078 RestCallbackMatcher matcher(uri);
5079
5080 PImpl::ChunkedRestCallback* callback = NULL;
5081
5082 // Loop over the callbacks registered by the plugins
5083 for (PImpl::ChunkedRestCallbacks::const_iterator it = pimpl_->chunkedRestCallbacks_.begin();
5084 it != pimpl_->chunkedRestCallbacks_.end(); ++it)
5085 {
5086 if (matcher.IsMatch((*it)->GetRegularExpression()))
5087 {
5088 callback = *it;
5089 break;
5090 }
5091 }
5092
5093 if (callback == NULL)
5094 {
5095 // Callback not found
5096 return false;
5097 }
5098 else
5099 {
5100 OrthancPluginServerChunkedRequestReaderFactory handler;
5101
5102 switch (method)
5103 {
5104 case HttpMethod_Post:
5105 handler = callback->GetParameters().postHandler;
5106 break;
5107
5108 case HttpMethod_Put:
5109 handler = callback->GetParameters().putHandler;
5110 break;
5111
5112 default:
5113 handler = NULL;
5114 break;
5115 }
5116
5117 if (handler == NULL)
5118 {
5119 return false;
5120 }
5121 else
5122 {
5123 LOG(INFO) << "Delegating chunked HTTP request to plugin for URI: " << matcher.GetFlatUri();
5124
5125 HttpRequestConverter converter(matcher, method, headers);
5126 converter.GetRequest().body = NULL;
5127 converter.GetRequest().bodySize = 0;
5128
5129 OrthancPluginServerChunkedRequestReader* reader = NULL;
5130
5131 OrthancPluginErrorCode errorCode = handler(
5132 &reader, matcher.GetFlatUri().c_str(), &converter.GetRequest());
5133
5134 if (reader == NULL)
5135 {
5136 // The plugin has not created a reader for chunked body
5137 return false;
5138 }
5139 else if (errorCode != OrthancPluginErrorCode_Success)
5140 {
5141 throw OrthancException(static_cast<ErrorCode>(errorCode));
5142 }
5143 else
5144 {
5145 target.reset(new HttpServerChunkedReader(reader, callback->GetParameters(), GetErrorDictionary()));
5146 return true;
5147 }
5148 }
5149 }
5150 }
5151
5152
5153 IStorageCommitmentFactory::ILookupHandler* OrthancPlugins::CreateStorageCommitment(
5154 const std::string& jobId,
5155 const std::string& transactionUid,
5156 const std::vector<std::string>& sopClassUids,
5157 const std::vector<std::string>& sopInstanceUids,
5158 const std::string& remoteAet,
5159 const std::string& calledAet)
5160 {
5161 boost::mutex::scoped_lock lock(pimpl_->storageCommitmentScpMutex_);
5162
5163 for (PImpl::StorageCommitmentScpCallbacks::iterator
5164 it = pimpl_->storageCommitmentScpCallbacks_.begin();
5165 it != pimpl_->storageCommitmentScpCallbacks_.end(); ++it)
5166 {
5167 assert(*it != NULL);
5168 IStorageCommitmentFactory::ILookupHandler* handler = (*it)->CreateStorageCommitment
5169 (jobId, transactionUid, sopClassUids, sopInstanceUids, remoteAet, calledAet);
5170
5171 if (handler != NULL)
5172 {
5173 return handler;
5174 }
5175 }
5176
5177 return NULL;
5178 }
5179
5180
5181 class MemoryBufferRaii : public boost::noncopyable
5182 {
5183 private:
5184 OrthancPluginMemoryBuffer buffer_;
5185
5186 public:
5187 MemoryBufferRaii()
5188 {
5189 buffer_.size = 0;
5190 buffer_.data = NULL;
5191 }
5192
5193 ~MemoryBufferRaii()
5194 {
5195 if (buffer_.size != 0)
5196 {
5197 free(buffer_.data);
5198 }
5199 }
5200
5201 OrthancPluginMemoryBuffer* GetObject()
5202 {
5203 return &buffer_;
5204 }
5205
5206 void ToString(std::string& target) const
5207 {
5208 target.resize(buffer_.size);
5209
5210 if (buffer_.size != 0)
5211 {
5212 memcpy(&target[0], buffer_.data, buffer_.size);
5213 }
5214 }
5215 };
5216
5217
5218 bool OrthancPlugins::TranscodeBuffer(std::string& target,
5219 const void* buffer,
5220 size_t size,
5221 const std::set<DicomTransferSyntax>& allowedSyntaxes,
5222 bool allowNewSopInstanceUid)
5223 {
5224 boost::shared_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
5225
5226 if (pimpl_->transcoderCallbacks_.empty())
5227 {
5228 return false;
5229 }
5230
5231 std::vector<const char*> uids;
5232 uids.reserve(allowedSyntaxes.size());
5233 for (std::set<DicomTransferSyntax>::const_iterator
5234 it = allowedSyntaxes.begin(); it != allowedSyntaxes.end(); ++it)
5235 {
5236 uids.push_back(GetTransferSyntaxUid(*it));
5237 }
5238
5239 for (PImpl::TranscoderCallbacks::const_iterator
5240 transcoder = pimpl_->transcoderCallbacks_.begin();
5241 transcoder != pimpl_->transcoderCallbacks_.end(); ++transcoder)
5242 {
5243 MemoryBufferRaii a;
5244
5245 if ((*transcoder) (a.GetObject(), buffer, size, uids.empty() ? NULL : &uids[0],
5246 static_cast<uint32_t>(uids.size()), allowNewSopInstanceUid) ==
5247 OrthancPluginErrorCode_Success)
5248 {
5249 a.ToString(target);
5250 return true;
5251 }
5252 }
5253
5254 return false;
5255 }
5256 }