Mercurial > hg > orthanc
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 } |