comparison RenderingPlugin/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp @ 1877:a2955abe4c2e

skeleton for the RenderingPlugin
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 12 Jan 2022 08:23:38 +0100
parents Applications/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp@7053b8a0aaec
children 6a65b7cb77ab
comparison
equal deleted inserted replaced
1876:b1f510e601d2 1877:a2955abe4c2e
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-2022 Osimis S.A., Belgium
6 * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
7 *
8 * This program is free software: you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation, either version 3 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 **/
21
22
23 #include "OrthancPluginCppWrapper.h"
24
25 #include <boost/algorithm/string/predicate.hpp>
26 #include <boost/move/unique_ptr.hpp>
27 #include <boost/thread.hpp>
28
29
30 #include <json/reader.h>
31 #include <json/version.h>
32 #include <json/writer.h>
33
34 #if !defined(JSONCPP_VERSION_MAJOR) || !defined(JSONCPP_VERSION_MINOR)
35 # error Cannot access the version of JsonCpp
36 #endif
37
38
39 /**
40 * We use deprecated "Json::Reader", "Json::StyledWriter" and
41 * "Json::FastWriter" if JsonCpp < 1.7.0. This choice is rather
42 * arbitrary, but if Json >= 1.9.0, gcc generates explicit deprecation
43 * warnings (clang was warning in earlier versions). For reference,
44 * these classes seem to have been deprecated since JsonCpp 1.4.0 (on
45 * February 2015) by the following changeset:
46 * https://github.com/open-source-parsers/jsoncpp/commit/8df98f6112890d6272734975dd6d70cf8999bb22
47 **/
48 #if (JSONCPP_VERSION_MAJOR >= 2 || \
49 (JSONCPP_VERSION_MAJOR == 1 && JSONCPP_VERSION_MINOR >= 8))
50 # define JSONCPP_USE_DEPRECATED 0
51 #else
52 # define JSONCPP_USE_DEPRECATED 1
53 #endif
54
55
56 #if !ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 0)
57 static const OrthancPluginErrorCode OrthancPluginErrorCode_NullPointer = OrthancPluginErrorCode_Plugin;
58 #endif
59
60
61 namespace OrthancPlugins
62 {
63 static OrthancPluginContext* globalContext_ = NULL;
64
65
66 void SetGlobalContext(OrthancPluginContext* context)
67 {
68 if (context == NULL)
69 {
70 ORTHANC_PLUGINS_THROW_EXCEPTION(NullPointer);
71 }
72 else if (globalContext_ == NULL)
73 {
74 globalContext_ = context;
75 }
76 else
77 {
78 ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
79 }
80 }
81
82
83 bool HasGlobalContext()
84 {
85 return globalContext_ != NULL;
86 }
87
88
89 OrthancPluginContext* GetGlobalContext()
90 {
91 if (globalContext_ == NULL)
92 {
93 ORTHANC_PLUGINS_THROW_EXCEPTION(BadSequenceOfCalls);
94 }
95 else
96 {
97 return globalContext_;
98 }
99 }
100
101
102 void MemoryBuffer::Check(OrthancPluginErrorCode code)
103 {
104 if (code != OrthancPluginErrorCode_Success)
105 {
106 // Prevent using garbage information
107 buffer_.data = NULL;
108 buffer_.size = 0;
109 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
110 }
111 }
112
113
114 bool MemoryBuffer::CheckHttp(OrthancPluginErrorCode code)
115 {
116 if (code != OrthancPluginErrorCode_Success)
117 {
118 // Prevent using garbage information
119 buffer_.data = NULL;
120 buffer_.size = 0;
121 }
122
123 if (code == OrthancPluginErrorCode_Success)
124 {
125 return true;
126 }
127 else if (code == OrthancPluginErrorCode_UnknownResource ||
128 code == OrthancPluginErrorCode_InexistentItem)
129 {
130 return false;
131 }
132 else
133 {
134 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
135 }
136 }
137
138
139 MemoryBuffer::MemoryBuffer()
140 {
141 buffer_.data = NULL;
142 buffer_.size = 0;
143 }
144
145
146 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
147 MemoryBuffer::MemoryBuffer(const void* buffer,
148 size_t size)
149 {
150 uint32_t s = static_cast<uint32_t>(size);
151 if (static_cast<size_t>(s) != size)
152 {
153 ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory);
154 }
155 else if (OrthancPluginCreateMemoryBuffer(GetGlobalContext(), &buffer_, s) !=
156 OrthancPluginErrorCode_Success)
157 {
158 ORTHANC_PLUGINS_THROW_EXCEPTION(NotEnoughMemory);
159 }
160 else
161 {
162 memcpy(buffer_.data, buffer, size);
163 }
164 }
165 #endif
166
167
168 void MemoryBuffer::Clear()
169 {
170 if (buffer_.data != NULL)
171 {
172 OrthancPluginFreeMemoryBuffer(GetGlobalContext(), &buffer_);
173 buffer_.data = NULL;
174 buffer_.size = 0;
175 }
176 }
177
178
179 void MemoryBuffer::Assign(OrthancPluginMemoryBuffer& other)
180 {
181 Clear();
182
183 buffer_.data = other.data;
184 buffer_.size = other.size;
185
186 other.data = NULL;
187 other.size = 0;
188 }
189
190
191 void MemoryBuffer::Swap(MemoryBuffer& other)
192 {
193 std::swap(buffer_.data, other.buffer_.data);
194 std::swap(buffer_.size, other.buffer_.size);
195 }
196
197
198 OrthancPluginMemoryBuffer MemoryBuffer::Release()
199 {
200 OrthancPluginMemoryBuffer result = buffer_;
201
202 buffer_.data = NULL;
203 buffer_.size = 0;
204
205 return result;
206 }
207
208
209 void MemoryBuffer::ToString(std::string& target) const
210 {
211 if (buffer_.size == 0)
212 {
213 target.clear();
214 }
215 else
216 {
217 target.assign(reinterpret_cast<const char*>(buffer_.data), buffer_.size);
218 }
219 }
220
221
222 void MemoryBuffer::ToJson(Json::Value& target) const
223 {
224 if (buffer_.data == NULL ||
225 buffer_.size == 0)
226 {
227 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
228 }
229
230 if (!ReadJson(target, buffer_.data, buffer_.size))
231 {
232 LogError("Cannot convert some memory buffer to JSON");
233 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
234 }
235 }
236
237
238 bool MemoryBuffer::RestApiGet(const std::string& uri,
239 bool applyPlugins)
240 {
241 Clear();
242
243 if (applyPlugins)
244 {
245 return CheckHttp(OrthancPluginRestApiGetAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str()));
246 }
247 else
248 {
249 return CheckHttp(OrthancPluginRestApiGet(GetGlobalContext(), &buffer_, uri.c_str()));
250 }
251 }
252
253 bool MemoryBuffer::RestApiGet(const std::string& uri,
254 const std::map<std::string, std::string>& httpHeaders,
255 bool applyPlugins)
256 {
257 Clear();
258
259 std::vector<const char*> headersKeys;
260 std::vector<const char*> headersValues;
261
262 for (std::map<std::string, std::string>::const_iterator
263 it = httpHeaders.begin(); it != httpHeaders.end(); it++)
264 {
265 headersKeys.push_back(it->first.c_str());
266 headersValues.push_back(it->second.c_str());
267 }
268
269 return CheckHttp(OrthancPluginRestApiGet2(
270 GetGlobalContext(), &buffer_, uri.c_str(), httpHeaders.size(),
271 (headersKeys.empty() ? NULL : &headersKeys[0]),
272 (headersValues.empty() ? NULL : &headersValues[0]), applyPlugins));
273 }
274
275 bool MemoryBuffer::RestApiPost(const std::string& uri,
276 const void* body,
277 size_t bodySize,
278 bool applyPlugins)
279 {
280 Clear();
281
282 // Cast for compatibility with Orthanc SDK <= 1.5.6
283 const char* b = reinterpret_cast<const char*>(body);
284
285 if (applyPlugins)
286 {
287 return CheckHttp(OrthancPluginRestApiPostAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
288 }
289 else
290 {
291 return CheckHttp(OrthancPluginRestApiPost(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
292 }
293 }
294
295
296 bool MemoryBuffer::RestApiPut(const std::string& uri,
297 const void* body,
298 size_t bodySize,
299 bool applyPlugins)
300 {
301 Clear();
302
303 // Cast for compatibility with Orthanc SDK <= 1.5.6
304 const char* b = reinterpret_cast<const char*>(body);
305
306 if (applyPlugins)
307 {
308 return CheckHttp(OrthancPluginRestApiPutAfterPlugins(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
309 }
310 else
311 {
312 return CheckHttp(OrthancPluginRestApiPut(GetGlobalContext(), &buffer_, uri.c_str(), b, bodySize));
313 }
314 }
315
316
317 static bool ReadJsonInternal(Json::Value& target,
318 const void* buffer,
319 size_t size,
320 bool collectComments)
321 {
322 #if JSONCPP_USE_DEPRECATED == 1
323 Json::Reader reader;
324 return reader.parse(reinterpret_cast<const char*>(buffer),
325 reinterpret_cast<const char*>(buffer) + size, target, collectComments);
326 #else
327 Json::CharReaderBuilder builder;
328 builder.settings_["collectComments"] = collectComments;
329
330 const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
331 assert(reader.get() != NULL);
332
333 JSONCPP_STRING err;
334 if (reader->parse(reinterpret_cast<const char*>(buffer),
335 reinterpret_cast<const char*>(buffer) + size, &target, &err))
336 {
337 return true;
338 }
339 else
340 {
341 LogError("Cannot parse JSON: " + std::string(err));
342 return false;
343 }
344 #endif
345 }
346
347
348 bool ReadJson(Json::Value& target,
349 const std::string& source)
350 {
351 return ReadJson(target, source.empty() ? NULL : source.c_str(), source.size());
352 }
353
354
355 bool ReadJson(Json::Value& target,
356 const void* buffer,
357 size_t size)
358 {
359 return ReadJsonInternal(target, buffer, size, true);
360 }
361
362
363 bool ReadJsonWithoutComments(Json::Value& target,
364 const std::string& source)
365 {
366 return ReadJsonWithoutComments(target, source.empty() ? NULL : source.c_str(), source.size());
367 }
368
369
370 bool ReadJsonWithoutComments(Json::Value& target,
371 const void* buffer,
372 size_t size)
373 {
374 return ReadJsonInternal(target, buffer, size, false);
375 }
376
377
378 void WriteFastJson(std::string& target,
379 const Json::Value& source)
380 {
381 #if JSONCPP_USE_DEPRECATED == 1
382 Json::FastWriter writer;
383 target = writer.write(source);
384 #else
385 Json::StreamWriterBuilder builder;
386 builder.settings_["indentation"] = "";
387 target = Json::writeString(builder, source);
388 #endif
389 }
390
391
392 void WriteStyledJson(std::string& target,
393 const Json::Value& source)
394 {
395 #if JSONCPP_USE_DEPRECATED == 1
396 Json::StyledWriter writer;
397 target = writer.write(source);
398 #else
399 Json::StreamWriterBuilder builder;
400 builder.settings_["indentation"] = " ";
401 target = Json::writeString(builder, source);
402 #endif
403 }
404
405
406 bool MemoryBuffer::RestApiPost(const std::string& uri,
407 const Json::Value& body,
408 bool applyPlugins)
409 {
410 std::string s;
411 WriteFastJson(s, body);
412 return RestApiPost(uri, s, applyPlugins);
413 }
414
415
416 bool MemoryBuffer::RestApiPut(const std::string& uri,
417 const Json::Value& body,
418 bool applyPlugins)
419 {
420 std::string s;
421 WriteFastJson(s, body);
422 return RestApiPut(uri, s, applyPlugins);
423 }
424
425
426 void MemoryBuffer::CreateDicom(const Json::Value& tags,
427 OrthancPluginCreateDicomFlags flags)
428 {
429 Clear();
430
431 std::string s;
432 WriteFastJson(s, tags);
433
434 Check(OrthancPluginCreateDicom(GetGlobalContext(), &buffer_, s.c_str(), NULL, flags));
435 }
436
437 void MemoryBuffer::CreateDicom(const Json::Value& tags,
438 const OrthancImage& pixelData,
439 OrthancPluginCreateDicomFlags flags)
440 {
441 Clear();
442
443 std::string s;
444 WriteFastJson(s, tags);
445
446 Check(OrthancPluginCreateDicom(GetGlobalContext(), &buffer_, s.c_str(), pixelData.GetObject(), flags));
447 }
448
449
450 void MemoryBuffer::ReadFile(const std::string& path)
451 {
452 Clear();
453 Check(OrthancPluginReadFile(GetGlobalContext(), &buffer_, path.c_str()));
454 }
455
456
457 void MemoryBuffer::GetDicomQuery(const OrthancPluginWorklistQuery* query)
458 {
459 Clear();
460 Check(OrthancPluginWorklistGetDicomQuery(GetGlobalContext(), &buffer_, query));
461 }
462
463
464 void OrthancString::Assign(char* str)
465 {
466 Clear();
467
468 if (str != NULL)
469 {
470 str_ = str;
471 }
472 }
473
474
475 void OrthancString::Clear()
476 {
477 if (str_ != NULL)
478 {
479 OrthancPluginFreeString(GetGlobalContext(), str_);
480 str_ = NULL;
481 }
482 }
483
484
485 void OrthancString::ToString(std::string& target) const
486 {
487 if (str_ == NULL)
488 {
489 target.clear();
490 }
491 else
492 {
493 target.assign(str_);
494 }
495 }
496
497
498 void OrthancString::ToJson(Json::Value& target) const
499 {
500 if (str_ == NULL)
501 {
502 LogError("Cannot convert an empty memory buffer to JSON");
503 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
504 }
505
506 if (!ReadJson(target, str_))
507 {
508 LogError("Cannot convert some memory buffer to JSON");
509 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
510 }
511 }
512
513
514 void MemoryBuffer::DicomToJson(Json::Value& target,
515 OrthancPluginDicomToJsonFormat format,
516 OrthancPluginDicomToJsonFlags flags,
517 uint32_t maxStringLength)
518 {
519 OrthancString str;
520 str.Assign(OrthancPluginDicomBufferToJson
521 (GetGlobalContext(), GetData(), GetSize(), format, flags, maxStringLength));
522 str.ToJson(target);
523 }
524
525
526 bool MemoryBuffer::HttpGet(const std::string& url,
527 const std::string& username,
528 const std::string& password)
529 {
530 Clear();
531 return CheckHttp(OrthancPluginHttpGet(GetGlobalContext(), &buffer_, url.c_str(),
532 username.empty() ? NULL : username.c_str(),
533 password.empty() ? NULL : password.c_str()));
534 }
535
536
537 bool MemoryBuffer::HttpPost(const std::string& url,
538 const std::string& body,
539 const std::string& username,
540 const std::string& password)
541 {
542 Clear();
543
544 if (body.size() > 0xffffffffu)
545 {
546 LogError("Cannot handle body size > 4GB");
547 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
548 }
549
550 return CheckHttp(OrthancPluginHttpPost(GetGlobalContext(), &buffer_, url.c_str(),
551 body.c_str(), body.size(),
552 username.empty() ? NULL : username.c_str(),
553 password.empty() ? NULL : password.c_str()));
554 }
555
556
557 bool MemoryBuffer::HttpPut(const std::string& url,
558 const std::string& body,
559 const std::string& username,
560 const std::string& password)
561 {
562 Clear();
563
564 if (body.size() > 0xffffffffu)
565 {
566 LogError("Cannot handle body size > 4GB");
567 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
568 }
569
570 return CheckHttp(OrthancPluginHttpPut(GetGlobalContext(), &buffer_, url.c_str(),
571 body.empty() ? NULL : body.c_str(),
572 body.size(),
573 username.empty() ? NULL : username.c_str(),
574 password.empty() ? NULL : password.c_str()));
575 }
576
577
578 void MemoryBuffer::GetDicomInstance(const std::string& instanceId)
579 {
580 Clear();
581 Check(OrthancPluginGetDicomForInstance(GetGlobalContext(), &buffer_, instanceId.c_str()));
582 }
583
584
585 bool HttpDelete(const std::string& url,
586 const std::string& username,
587 const std::string& password)
588 {
589 OrthancPluginErrorCode error = OrthancPluginHttpDelete
590 (GetGlobalContext(), url.c_str(),
591 username.empty() ? NULL : username.c_str(),
592 password.empty() ? NULL : password.c_str());
593
594 if (error == OrthancPluginErrorCode_Success)
595 {
596 return true;
597 }
598 else if (error == OrthancPluginErrorCode_UnknownResource ||
599 error == OrthancPluginErrorCode_InexistentItem)
600 {
601 return false;
602 }
603 else
604 {
605 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
606 }
607 }
608
609
610 void LogError(const std::string& message)
611 {
612 if (HasGlobalContext())
613 {
614 OrthancPluginLogError(GetGlobalContext(), message.c_str());
615 }
616 }
617
618
619 void LogWarning(const std::string& message)
620 {
621 if (HasGlobalContext())
622 {
623 OrthancPluginLogWarning(GetGlobalContext(), message.c_str());
624 }
625 }
626
627
628 void LogInfo(const std::string& message)
629 {
630 if (HasGlobalContext())
631 {
632 OrthancPluginLogInfo(GetGlobalContext(), message.c_str());
633 }
634 }
635
636
637 void OrthancConfiguration::LoadConfiguration()
638 {
639 OrthancString str;
640 str.Assign(OrthancPluginGetConfiguration(GetGlobalContext()));
641
642 if (str.GetContent() == NULL)
643 {
644 LogError("Cannot access the Orthanc configuration");
645 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
646 }
647
648 str.ToJson(configuration_);
649
650 if (configuration_.type() != Json::objectValue)
651 {
652 LogError("Unable to read the Orthanc configuration");
653 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
654 }
655 }
656
657
658 OrthancConfiguration::OrthancConfiguration()
659 {
660 LoadConfiguration();
661 }
662
663
664 OrthancConfiguration::OrthancConfiguration(bool loadConfiguration)
665 {
666 if (loadConfiguration)
667 {
668 LoadConfiguration();
669 }
670 else
671 {
672 configuration_ = Json::objectValue;
673 }
674 }
675
676
677 std::string OrthancConfiguration::GetPath(const std::string& key) const
678 {
679 if (path_.empty())
680 {
681 return key;
682 }
683 else
684 {
685 return path_ + "." + key;
686 }
687 }
688
689
690 bool OrthancConfiguration::IsSection(const std::string& key) const
691 {
692 assert(configuration_.type() == Json::objectValue);
693
694 return (configuration_.isMember(key) &&
695 configuration_[key].type() == Json::objectValue);
696 }
697
698
699 void OrthancConfiguration::GetSection(OrthancConfiguration& target,
700 const std::string& key) const
701 {
702 assert(configuration_.type() == Json::objectValue);
703
704 target.path_ = GetPath(key);
705
706 if (!configuration_.isMember(key))
707 {
708 target.configuration_ = Json::objectValue;
709 }
710 else
711 {
712 if (configuration_[key].type() != Json::objectValue)
713 {
714 LogError("The configuration section \"" + target.path_ +
715 "\" is not an associative array as expected");
716
717 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
718 }
719
720 target.configuration_ = configuration_[key];
721 }
722 }
723
724
725 bool OrthancConfiguration::LookupStringValue(std::string& target,
726 const std::string& key) const
727 {
728 assert(configuration_.type() == Json::objectValue);
729
730 if (!configuration_.isMember(key))
731 {
732 return false;
733 }
734
735 if (configuration_[key].type() != Json::stringValue)
736 {
737 LogError("The configuration option \"" + GetPath(key) +
738 "\" is not a string as expected");
739
740 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
741 }
742
743 target = configuration_[key].asString();
744 return true;
745 }
746
747
748 bool OrthancConfiguration::LookupIntegerValue(int& target,
749 const std::string& key) const
750 {
751 assert(configuration_.type() == Json::objectValue);
752
753 if (!configuration_.isMember(key))
754 {
755 return false;
756 }
757
758 switch (configuration_[key].type())
759 {
760 case Json::intValue:
761 target = configuration_[key].asInt();
762 return true;
763
764 case Json::uintValue:
765 target = configuration_[key].asUInt();
766 return true;
767
768 default:
769 LogError("The configuration option \"" + GetPath(key) +
770 "\" is not an integer as expected");
771
772 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
773 }
774 }
775
776
777 bool OrthancConfiguration::LookupUnsignedIntegerValue(unsigned int& target,
778 const std::string& key) const
779 {
780 int tmp;
781 if (!LookupIntegerValue(tmp, key))
782 {
783 return false;
784 }
785
786 if (tmp < 0)
787 {
788 LogError("The configuration option \"" + GetPath(key) +
789 "\" is not a positive integer as expected");
790
791 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
792 }
793 else
794 {
795 target = static_cast<unsigned int>(tmp);
796 return true;
797 }
798 }
799
800
801 bool OrthancConfiguration::LookupBooleanValue(bool& target,
802 const std::string& key) const
803 {
804 assert(configuration_.type() == Json::objectValue);
805
806 if (!configuration_.isMember(key))
807 {
808 return false;
809 }
810
811 if (configuration_[key].type() != Json::booleanValue)
812 {
813 LogError("The configuration option \"" + GetPath(key) +
814 "\" is not a Boolean as expected");
815
816 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
817 }
818
819 target = configuration_[key].asBool();
820 return true;
821 }
822
823
824 bool OrthancConfiguration::LookupFloatValue(float& target,
825 const std::string& key) const
826 {
827 assert(configuration_.type() == Json::objectValue);
828
829 if (!configuration_.isMember(key))
830 {
831 return false;
832 }
833
834 switch (configuration_[key].type())
835 {
836 case Json::realValue:
837 target = configuration_[key].asFloat();
838 return true;
839
840 case Json::intValue:
841 target = static_cast<float>(configuration_[key].asInt());
842 return true;
843
844 case Json::uintValue:
845 target = static_cast<float>(configuration_[key].asUInt());
846 return true;
847
848 default:
849 LogError("The configuration option \"" + GetPath(key) +
850 "\" is not an integer as expected");
851
852 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
853 }
854 }
855
856
857 bool OrthancConfiguration::LookupListOfStrings(std::list<std::string>& target,
858 const std::string& key,
859 bool allowSingleString) const
860 {
861 assert(configuration_.type() == Json::objectValue);
862
863 target.clear();
864
865 if (!configuration_.isMember(key))
866 {
867 return false;
868 }
869
870 switch (configuration_[key].type())
871 {
872 case Json::arrayValue:
873 {
874 bool ok = true;
875
876 for (Json::Value::ArrayIndex i = 0; ok && i < configuration_[key].size(); i++)
877 {
878 if (configuration_[key][i].type() == Json::stringValue)
879 {
880 target.push_back(configuration_[key][i].asString());
881 }
882 else
883 {
884 ok = false;
885 }
886 }
887
888 if (ok)
889 {
890 return true;
891 }
892
893 break;
894 }
895
896 case Json::stringValue:
897 if (allowSingleString)
898 {
899 target.push_back(configuration_[key].asString());
900 return true;
901 }
902
903 break;
904
905 default:
906 break;
907 }
908
909 LogError("The configuration option \"" + GetPath(key) +
910 "\" is not a list of strings as expected");
911
912 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
913 }
914
915
916 bool OrthancConfiguration::LookupSetOfStrings(std::set<std::string>& target,
917 const std::string& key,
918 bool allowSingleString) const
919 {
920 std::list<std::string> lst;
921
922 if (LookupListOfStrings(lst, key, allowSingleString))
923 {
924 target.clear();
925
926 for (std::list<std::string>::const_iterator
927 it = lst.begin(); it != lst.end(); ++it)
928 {
929 target.insert(*it);
930 }
931
932 return true;
933 }
934 else
935 {
936 return false;
937 }
938 }
939
940
941 std::string OrthancConfiguration::GetStringValue(const std::string& key,
942 const std::string& defaultValue) const
943 {
944 std::string tmp;
945 if (LookupStringValue(tmp, key))
946 {
947 return tmp;
948 }
949 else
950 {
951 return defaultValue;
952 }
953 }
954
955
956 int OrthancConfiguration::GetIntegerValue(const std::string& key,
957 int defaultValue) const
958 {
959 int tmp;
960 if (LookupIntegerValue(tmp, key))
961 {
962 return tmp;
963 }
964 else
965 {
966 return defaultValue;
967 }
968 }
969
970
971 unsigned int OrthancConfiguration::GetUnsignedIntegerValue(const std::string& key,
972 unsigned int defaultValue) const
973 {
974 unsigned int tmp;
975 if (LookupUnsignedIntegerValue(tmp, key))
976 {
977 return tmp;
978 }
979 else
980 {
981 return defaultValue;
982 }
983 }
984
985
986 bool OrthancConfiguration::GetBooleanValue(const std::string& key,
987 bool defaultValue) const
988 {
989 bool tmp;
990 if (LookupBooleanValue(tmp, key))
991 {
992 return tmp;
993 }
994 else
995 {
996 return defaultValue;
997 }
998 }
999
1000
1001 float OrthancConfiguration::GetFloatValue(const std::string& key,
1002 float defaultValue) const
1003 {
1004 float tmp;
1005 if (LookupFloatValue(tmp, key))
1006 {
1007 return tmp;
1008 }
1009 else
1010 {
1011 return defaultValue;
1012 }
1013 }
1014
1015
1016 void OrthancConfiguration::GetDictionary(std::map<std::string, std::string>& target,
1017 const std::string& key) const
1018 {
1019 assert(configuration_.type() == Json::objectValue);
1020
1021 target.clear();
1022
1023 if (!configuration_.isMember(key))
1024 {
1025 return;
1026 }
1027
1028 if (configuration_[key].type() != Json::objectValue)
1029 {
1030 LogError("The configuration option \"" + GetPath(key) +
1031 "\" is not a string as expected");
1032
1033 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
1034 }
1035
1036 Json::Value::Members members = configuration_[key].getMemberNames();
1037
1038 for (size_t i = 0; i < members.size(); i++)
1039 {
1040 const Json::Value& value = configuration_[key][members[i]];
1041
1042 if (value.type() == Json::stringValue)
1043 {
1044 target[members[i]] = value.asString();
1045 }
1046 else
1047 {
1048 LogError("The configuration option \"" + GetPath(key) +
1049 "\" is not a dictionary mapping strings to strings");
1050
1051 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
1052 }
1053 }
1054 }
1055
1056
1057 void OrthancImage::Clear()
1058 {
1059 if (image_ != NULL)
1060 {
1061 OrthancPluginFreeImage(GetGlobalContext(), image_);
1062 image_ = NULL;
1063 }
1064 }
1065
1066
1067 void OrthancImage::CheckImageAvailable() const
1068 {
1069 if (image_ == NULL)
1070 {
1071 LogError("Trying to access a NULL image");
1072 ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
1073 }
1074 }
1075
1076
1077 OrthancImage::OrthancImage() :
1078 image_(NULL)
1079 {
1080 }
1081
1082
1083 OrthancImage::OrthancImage(OrthancPluginImage* image) :
1084 image_(image)
1085 {
1086 }
1087
1088
1089 OrthancImage::OrthancImage(OrthancPluginPixelFormat format,
1090 uint32_t width,
1091 uint32_t height)
1092 {
1093 image_ = OrthancPluginCreateImage(GetGlobalContext(), format, width, height);
1094
1095 if (image_ == NULL)
1096 {
1097 LogError("Cannot create an image");
1098 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
1099 }
1100 }
1101
1102
1103 OrthancImage::OrthancImage(OrthancPluginPixelFormat format,
1104 uint32_t width,
1105 uint32_t height,
1106 uint32_t pitch,
1107 void* buffer)
1108 {
1109 image_ = OrthancPluginCreateImageAccessor
1110 (GetGlobalContext(), format, width, height, pitch, buffer);
1111
1112 if (image_ == NULL)
1113 {
1114 LogError("Cannot create an image accessor");
1115 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
1116 }
1117 }
1118
1119 void OrthancImage::UncompressPngImage(const void* data,
1120 size_t size)
1121 {
1122 Clear();
1123
1124 image_ = OrthancPluginUncompressImage(GetGlobalContext(), data, size, OrthancPluginImageFormat_Png);
1125
1126 if (image_ == NULL)
1127 {
1128 LogError("Cannot uncompress a PNG image");
1129 ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
1130 }
1131 }
1132
1133
1134 void OrthancImage::UncompressJpegImage(const void* data,
1135 size_t size)
1136 {
1137 Clear();
1138 image_ = OrthancPluginUncompressImage(GetGlobalContext(), data, size, OrthancPluginImageFormat_Jpeg);
1139 if (image_ == NULL)
1140 {
1141 LogError("Cannot uncompress a JPEG image");
1142 ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
1143 }
1144 }
1145
1146
1147 void OrthancImage::DecodeDicomImage(const void* data,
1148 size_t size,
1149 unsigned int frame)
1150 {
1151 Clear();
1152 image_ = OrthancPluginDecodeDicomImage(GetGlobalContext(), data, size, frame);
1153 if (image_ == NULL)
1154 {
1155 LogError("Cannot uncompress a DICOM image");
1156 ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
1157 }
1158 }
1159
1160
1161 OrthancPluginPixelFormat OrthancImage::GetPixelFormat() const
1162 {
1163 CheckImageAvailable();
1164 return OrthancPluginGetImagePixelFormat(GetGlobalContext(), image_);
1165 }
1166
1167
1168 unsigned int OrthancImage::GetWidth() const
1169 {
1170 CheckImageAvailable();
1171 return OrthancPluginGetImageWidth(GetGlobalContext(), image_);
1172 }
1173
1174
1175 unsigned int OrthancImage::GetHeight() const
1176 {
1177 CheckImageAvailable();
1178 return OrthancPluginGetImageHeight(GetGlobalContext(), image_);
1179 }
1180
1181
1182 unsigned int OrthancImage::GetPitch() const
1183 {
1184 CheckImageAvailable();
1185 return OrthancPluginGetImagePitch(GetGlobalContext(), image_);
1186 }
1187
1188
1189 void* OrthancImage::GetBuffer() const
1190 {
1191 CheckImageAvailable();
1192 return OrthancPluginGetImageBuffer(GetGlobalContext(), image_);
1193 }
1194
1195
1196 void OrthancImage::CompressPngImage(MemoryBuffer& target) const
1197 {
1198 CheckImageAvailable();
1199
1200 OrthancPlugins::MemoryBuffer answer;
1201 OrthancPluginCompressPngImage(GetGlobalContext(), *answer, GetPixelFormat(),
1202 GetWidth(), GetHeight(), GetPitch(), GetBuffer());
1203
1204 target.Swap(answer);
1205 }
1206
1207
1208 void OrthancImage::CompressJpegImage(MemoryBuffer& target,
1209 uint8_t quality) const
1210 {
1211 CheckImageAvailable();
1212
1213 OrthancPlugins::MemoryBuffer answer;
1214 OrthancPluginCompressJpegImage(GetGlobalContext(), *answer, GetPixelFormat(),
1215 GetWidth(), GetHeight(), GetPitch(), GetBuffer(), quality);
1216
1217 target.Swap(answer);
1218 }
1219
1220
1221 void OrthancImage::AnswerPngImage(OrthancPluginRestOutput* output) const
1222 {
1223 CheckImageAvailable();
1224 OrthancPluginCompressAndAnswerPngImage(GetGlobalContext(), output, GetPixelFormat(),
1225 GetWidth(), GetHeight(), GetPitch(), GetBuffer());
1226 }
1227
1228
1229 void OrthancImage::AnswerJpegImage(OrthancPluginRestOutput* output,
1230 uint8_t quality) const
1231 {
1232 CheckImageAvailable();
1233 OrthancPluginCompressAndAnswerJpegImage(GetGlobalContext(), output, GetPixelFormat(),
1234 GetWidth(), GetHeight(), GetPitch(), GetBuffer(), quality);
1235 }
1236
1237
1238 OrthancPluginImage* OrthancImage::Release()
1239 {
1240 CheckImageAvailable();
1241 OrthancPluginImage* tmp = image_;
1242 image_ = NULL;
1243 return tmp;
1244 }
1245
1246
1247 #if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
1248 FindMatcher::FindMatcher(const OrthancPluginWorklistQuery* worklist) :
1249 matcher_(NULL),
1250 worklist_(worklist)
1251 {
1252 if (worklist_ == NULL)
1253 {
1254 ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
1255 }
1256 }
1257
1258
1259 void FindMatcher::SetupDicom(const void* query,
1260 uint32_t size)
1261 {
1262 worklist_ = NULL;
1263
1264 matcher_ = OrthancPluginCreateFindMatcher(GetGlobalContext(), query, size);
1265 if (matcher_ == NULL)
1266 {
1267 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
1268 }
1269 }
1270
1271
1272 FindMatcher::~FindMatcher()
1273 {
1274 // The "worklist_" field
1275
1276 if (matcher_ != NULL)
1277 {
1278 OrthancPluginFreeFindMatcher(GetGlobalContext(), matcher_);
1279 }
1280 }
1281
1282
1283
1284 bool FindMatcher::IsMatch(const void* dicom,
1285 uint32_t size) const
1286 {
1287 int32_t result;
1288
1289 if (matcher_ != NULL)
1290 {
1291 result = OrthancPluginFindMatcherIsMatch(GetGlobalContext(), matcher_, dicom, size);
1292 }
1293 else if (worklist_ != NULL)
1294 {
1295 result = OrthancPluginWorklistIsMatch(GetGlobalContext(), worklist_, dicom, size);
1296 }
1297 else
1298 {
1299 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
1300 }
1301
1302 if (result == 0)
1303 {
1304 return false;
1305 }
1306 else if (result == 1)
1307 {
1308 return true;
1309 }
1310 else
1311 {
1312 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
1313 }
1314 }
1315
1316 #endif /* HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1 */
1317
1318 void AnswerJson(const Json::Value& value,
1319 OrthancPluginRestOutput* output)
1320 {
1321 std::string bodyString;
1322 WriteStyledJson(bodyString, value);
1323 OrthancPluginAnswerBuffer(GetGlobalContext(), output, bodyString.c_str(), bodyString.size(), "application/json");
1324 }
1325
1326 void AnswerString(const std::string& answer,
1327 const char* mimeType,
1328 OrthancPluginRestOutput* output)
1329 {
1330 OrthancPluginAnswerBuffer(GetGlobalContext(), output, answer.c_str(), answer.size(), mimeType);
1331 }
1332
1333 void AnswerHttpError(uint16_t httpError, OrthancPluginRestOutput *output)
1334 {
1335 OrthancPluginSendHttpStatusCode(GetGlobalContext(), output, httpError);
1336 }
1337
1338 void AnswerMethodNotAllowed(OrthancPluginRestOutput *output, const char* allowedMethods)
1339 {
1340 OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowedMethods);
1341 }
1342
1343 bool RestApiGetString(std::string& result,
1344 const std::string& uri,
1345 bool applyPlugins)
1346 {
1347 MemoryBuffer answer;
1348 if (!answer.RestApiGet(uri, applyPlugins))
1349 {
1350 return false;
1351 }
1352 else
1353 {
1354 answer.ToString(result);
1355 return true;
1356 }
1357 }
1358
1359 bool RestApiGetString(std::string& result,
1360 const std::string& uri,
1361 const std::map<std::string, std::string>& httpHeaders,
1362 bool applyPlugins)
1363 {
1364 MemoryBuffer answer;
1365 if (!answer.RestApiGet(uri, httpHeaders, applyPlugins))
1366 {
1367 return false;
1368 }
1369 else
1370 {
1371 answer.ToString(result);
1372 return true;
1373 }
1374 }
1375
1376
1377
1378 bool RestApiGet(Json::Value& result,
1379 const std::string& uri,
1380 bool applyPlugins)
1381 {
1382 MemoryBuffer answer;
1383
1384 if (!answer.RestApiGet(uri, applyPlugins))
1385 {
1386 return false;
1387 }
1388 else
1389 {
1390 if (!answer.IsEmpty())
1391 {
1392 answer.ToJson(result);
1393 }
1394 return true;
1395 }
1396 }
1397
1398
1399 bool RestApiPost(std::string& result,
1400 const std::string& uri,
1401 const void* body,
1402 size_t bodySize,
1403 bool applyPlugins)
1404 {
1405 MemoryBuffer answer;
1406
1407 if (!answer.RestApiPost(uri, body, bodySize, applyPlugins))
1408 {
1409 return false;
1410 }
1411 else
1412 {
1413 if (!answer.IsEmpty())
1414 {
1415 result.assign(answer.GetData(), answer.GetSize());
1416 }
1417 return true;
1418 }
1419 }
1420
1421
1422 bool RestApiPost(Json::Value& result,
1423 const std::string& uri,
1424 const void* body,
1425 size_t bodySize,
1426 bool applyPlugins)
1427 {
1428 MemoryBuffer answer;
1429
1430 if (!answer.RestApiPost(uri, body, bodySize, applyPlugins))
1431 {
1432 return false;
1433 }
1434 else
1435 {
1436 if (!answer.IsEmpty())
1437 {
1438 answer.ToJson(result);
1439 }
1440 return true;
1441 }
1442 }
1443
1444
1445 bool RestApiPost(Json::Value& result,
1446 const std::string& uri,
1447 const Json::Value& body,
1448 bool applyPlugins)
1449 {
1450 std::string s;
1451 WriteFastJson(s, body);
1452 return RestApiPost(result, uri, s, applyPlugins);
1453 }
1454
1455
1456 bool RestApiPut(Json::Value& result,
1457 const std::string& uri,
1458 const void* body,
1459 size_t bodySize,
1460 bool applyPlugins)
1461 {
1462 MemoryBuffer answer;
1463
1464 if (!answer.RestApiPut(uri, body, bodySize, applyPlugins))
1465 {
1466 return false;
1467 }
1468 else
1469 {
1470 if (!answer.IsEmpty()) // i.e, on a PUT to metadata/..., orthanc returns an empty response
1471 {
1472 answer.ToJson(result);
1473 }
1474 return true;
1475 }
1476 }
1477
1478
1479 bool RestApiPut(Json::Value& result,
1480 const std::string& uri,
1481 const Json::Value& body,
1482 bool applyPlugins)
1483 {
1484 std::string s;
1485 WriteFastJson(s, body);
1486 return RestApiPut(result, uri, s, applyPlugins);
1487 }
1488
1489
1490 bool RestApiDelete(const std::string& uri,
1491 bool applyPlugins)
1492 {
1493 OrthancPluginErrorCode error;
1494
1495 if (applyPlugins)
1496 {
1497 error = OrthancPluginRestApiDeleteAfterPlugins(GetGlobalContext(), uri.c_str());
1498 }
1499 else
1500 {
1501 error = OrthancPluginRestApiDelete(GetGlobalContext(), uri.c_str());
1502 }
1503
1504 if (error == OrthancPluginErrorCode_Success)
1505 {
1506 return true;
1507 }
1508 else if (error == OrthancPluginErrorCode_UnknownResource ||
1509 error == OrthancPluginErrorCode_InexistentItem)
1510 {
1511 return false;
1512 }
1513 else
1514 {
1515 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
1516 }
1517 }
1518
1519
1520 void ReportMinimalOrthancVersion(unsigned int major,
1521 unsigned int minor,
1522 unsigned int revision)
1523 {
1524 LogError("Your version of the Orthanc core (" +
1525 std::string(GetGlobalContext()->orthancVersion) +
1526 ") is too old to run this plugin (version " +
1527 boost::lexical_cast<std::string>(major) + "." +
1528 boost::lexical_cast<std::string>(minor) + "." +
1529 boost::lexical_cast<std::string>(revision) +
1530 " is required)");
1531 }
1532
1533
1534 bool CheckMinimalOrthancVersion(unsigned int major,
1535 unsigned int minor,
1536 unsigned int revision)
1537 {
1538 if (!HasGlobalContext())
1539 {
1540 LogError("Bad Orthanc context in the plugin");
1541 return false;
1542 }
1543
1544 if (!strcmp(GetGlobalContext()->orthancVersion, "mainline"))
1545 {
1546 // Assume compatibility with the mainline
1547 return true;
1548 }
1549
1550 // Parse the version of the Orthanc core
1551 int aa, bb, cc;
1552 if (
1553 #ifdef _MSC_VER
1554 sscanf_s
1555 #else
1556 sscanf
1557 #endif
1558 (GetGlobalContext()->orthancVersion, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 ||
1559 aa < 0 ||
1560 bb < 0 ||
1561 cc < 0)
1562 {
1563 return false;
1564 }
1565
1566 unsigned int a = static_cast<unsigned int>(aa);
1567 unsigned int b = static_cast<unsigned int>(bb);
1568 unsigned int c = static_cast<unsigned int>(cc);
1569
1570 // Check the major version number
1571
1572 if (a > major)
1573 {
1574 return true;
1575 }
1576
1577 if (a < major)
1578 {
1579 return false;
1580 }
1581
1582
1583 // Check the minor version number
1584 assert(a == major);
1585
1586 if (b > minor)
1587 {
1588 return true;
1589 }
1590
1591 if (b < minor)
1592 {
1593 return false;
1594 }
1595
1596 // Check the patch level version number
1597 assert(a == major && b == minor);
1598
1599 if (c >= revision)
1600 {
1601 return true;
1602 }
1603 else
1604 {
1605 return false;
1606 }
1607 }
1608
1609
1610 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
1611 const char* AutodetectMimeType(const std::string& path)
1612 {
1613 const char* mime = OrthancPluginAutodetectMimeType(GetGlobalContext(), path.c_str());
1614
1615 if (mime == NULL)
1616 {
1617 // Should never happen, just for safety
1618 return "application/octet-stream";
1619 }
1620 else
1621 {
1622 return mime;
1623 }
1624 }
1625 #endif
1626
1627
1628 #if HAS_ORTHANC_PLUGIN_PEERS == 1
1629 size_t OrthancPeers::GetPeerIndex(const std::string& name) const
1630 {
1631 size_t index;
1632 if (LookupName(index, name))
1633 {
1634 return index;
1635 }
1636 else
1637 {
1638 LogError("Inexistent peer: " + name);
1639 ORTHANC_PLUGINS_THROW_EXCEPTION(UnknownResource);
1640 }
1641 }
1642
1643
1644 OrthancPeers::OrthancPeers() :
1645 peers_(NULL),
1646 timeout_(0)
1647 {
1648 peers_ = OrthancPluginGetPeers(GetGlobalContext());
1649
1650 if (peers_ == NULL)
1651 {
1652 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
1653 }
1654
1655 uint32_t count = OrthancPluginGetPeersCount(GetGlobalContext(), peers_);
1656
1657 for (uint32_t i = 0; i < count; i++)
1658 {
1659 const char* name = OrthancPluginGetPeerName(GetGlobalContext(), peers_, i);
1660 if (name == NULL)
1661 {
1662 OrthancPluginFreePeers(GetGlobalContext(), peers_);
1663 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
1664 }
1665
1666 index_[name] = i;
1667 }
1668 }
1669
1670
1671 OrthancPeers::~OrthancPeers()
1672 {
1673 if (peers_ != NULL)
1674 {
1675 OrthancPluginFreePeers(GetGlobalContext(), peers_);
1676 }
1677 }
1678
1679
1680 bool OrthancPeers::LookupName(size_t& target,
1681 const std::string& name) const
1682 {
1683 Index::const_iterator found = index_.find(name);
1684
1685 if (found == index_.end())
1686 {
1687 return false;
1688 }
1689 else
1690 {
1691 target = found->second;
1692 return true;
1693 }
1694 }
1695
1696
1697 std::string OrthancPeers::GetPeerName(size_t index) const
1698 {
1699 if (index >= index_.size())
1700 {
1701 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
1702 }
1703 else
1704 {
1705 const char* s = OrthancPluginGetPeerName(GetGlobalContext(), peers_, static_cast<uint32_t>(index));
1706 if (s == NULL)
1707 {
1708 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
1709 }
1710 else
1711 {
1712 return s;
1713 }
1714 }
1715 }
1716
1717
1718 std::string OrthancPeers::GetPeerUrl(size_t index) const
1719 {
1720 if (index >= index_.size())
1721 {
1722 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
1723 }
1724 else
1725 {
1726 const char* s = OrthancPluginGetPeerUrl(GetGlobalContext(), peers_, static_cast<uint32_t>(index));
1727 if (s == NULL)
1728 {
1729 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
1730 }
1731 else
1732 {
1733 return s;
1734 }
1735 }
1736 }
1737
1738
1739 std::string OrthancPeers::GetPeerUrl(const std::string& name) const
1740 {
1741 return GetPeerUrl(GetPeerIndex(name));
1742 }
1743
1744
1745 bool OrthancPeers::LookupUserProperty(std::string& value,
1746 size_t index,
1747 const std::string& key) const
1748 {
1749 if (index >= index_.size())
1750 {
1751 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
1752 }
1753 else
1754 {
1755 const char* s = OrthancPluginGetPeerUserProperty(GetGlobalContext(), peers_, static_cast<uint32_t>(index), key.c_str());
1756 if (s == NULL)
1757 {
1758 return false;
1759 }
1760 else
1761 {
1762 value.assign(s);
1763 return true;
1764 }
1765 }
1766 }
1767
1768
1769 bool OrthancPeers::LookupUserProperty(std::string& value,
1770 const std::string& peer,
1771 const std::string& key) const
1772 {
1773 return LookupUserProperty(value, GetPeerIndex(peer), key);
1774 }
1775
1776
1777 bool OrthancPeers::DoGet(MemoryBuffer& target,
1778 size_t index,
1779 const std::string& uri) const
1780 {
1781 if (index >= index_.size())
1782 {
1783 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
1784 }
1785
1786 OrthancPlugins::MemoryBuffer answer;
1787 uint16_t status;
1788 OrthancPluginErrorCode code = OrthancPluginCallPeerApi
1789 (GetGlobalContext(), *answer, NULL, &status, peers_,
1790 static_cast<uint32_t>(index), OrthancPluginHttpMethod_Get, uri.c_str(),
1791 0, NULL, NULL, NULL, 0, timeout_);
1792
1793 if (code == OrthancPluginErrorCode_Success)
1794 {
1795 target.Swap(answer);
1796 return (status == 200);
1797 }
1798 else
1799 {
1800 return false;
1801 }
1802 }
1803
1804
1805 bool OrthancPeers::DoGet(MemoryBuffer& target,
1806 const std::string& name,
1807 const std::string& uri) const
1808 {
1809 size_t index;
1810 return (LookupName(index, name) &&
1811 DoGet(target, index, uri));
1812 }
1813
1814
1815 bool OrthancPeers::DoGet(Json::Value& target,
1816 size_t index,
1817 const std::string& uri) const
1818 {
1819 MemoryBuffer buffer;
1820
1821 if (DoGet(buffer, index, uri))
1822 {
1823 buffer.ToJson(target);
1824 return true;
1825 }
1826 else
1827 {
1828 return false;
1829 }
1830 }
1831
1832
1833 bool OrthancPeers::DoGet(Json::Value& target,
1834 const std::string& name,
1835 const std::string& uri) const
1836 {
1837 MemoryBuffer buffer;
1838
1839 if (DoGet(buffer, name, uri))
1840 {
1841 buffer.ToJson(target);
1842 return true;
1843 }
1844 else
1845 {
1846 return false;
1847 }
1848 }
1849
1850
1851 bool OrthancPeers::DoPost(MemoryBuffer& target,
1852 const std::string& name,
1853 const std::string& uri,
1854 const std::string& body) const
1855 {
1856 size_t index;
1857 return (LookupName(index, name) &&
1858 DoPost(target, index, uri, body));
1859 }
1860
1861
1862 bool OrthancPeers::DoPost(Json::Value& target,
1863 size_t index,
1864 const std::string& uri,
1865 const std::string& body) const
1866 {
1867 MemoryBuffer buffer;
1868
1869 if (DoPost(buffer, index, uri, body))
1870 {
1871 buffer.ToJson(target);
1872 return true;
1873 }
1874 else
1875 {
1876 return false;
1877 }
1878 }
1879
1880
1881 bool OrthancPeers::DoPost(Json::Value& target,
1882 const std::string& name,
1883 const std::string& uri,
1884 const std::string& body) const
1885 {
1886 MemoryBuffer buffer;
1887
1888 if (DoPost(buffer, name, uri, body))
1889 {
1890 buffer.ToJson(target);
1891 return true;
1892 }
1893 else
1894 {
1895 return false;
1896 }
1897 }
1898
1899
1900 bool OrthancPeers::DoPost(MemoryBuffer& target,
1901 size_t index,
1902 const std::string& uri,
1903 const std::string& body) const
1904 {
1905 if (index >= index_.size())
1906 {
1907 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
1908 }
1909
1910 if (body.size() > 0xffffffffu)
1911 {
1912 LogError("Cannot handle body size > 4GB");
1913 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
1914 }
1915
1916 OrthancPlugins::MemoryBuffer answer;
1917 uint16_t status;
1918 OrthancPluginErrorCode code = OrthancPluginCallPeerApi
1919 (GetGlobalContext(), *answer, NULL, &status, peers_,
1920 static_cast<uint32_t>(index), OrthancPluginHttpMethod_Post, uri.c_str(),
1921 0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_);
1922
1923 if (code == OrthancPluginErrorCode_Success)
1924 {
1925 target.Swap(answer);
1926 return (status == 200);
1927 }
1928 else
1929 {
1930 return false;
1931 }
1932 }
1933
1934
1935 bool OrthancPeers::DoPut(size_t index,
1936 const std::string& uri,
1937 const std::string& body) const
1938 {
1939 if (index >= index_.size())
1940 {
1941 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
1942 }
1943
1944 if (body.size() > 0xffffffffu)
1945 {
1946 LogError("Cannot handle body size > 4GB");
1947 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
1948 }
1949
1950 OrthancPlugins::MemoryBuffer answer;
1951 uint16_t status;
1952 OrthancPluginErrorCode code = OrthancPluginCallPeerApi
1953 (GetGlobalContext(), *answer, NULL, &status, peers_,
1954 static_cast<uint32_t>(index), OrthancPluginHttpMethod_Put, uri.c_str(),
1955 0, NULL, NULL, body.empty() ? NULL : body.c_str(), body.size(), timeout_);
1956
1957 if (code == OrthancPluginErrorCode_Success)
1958 {
1959 return (status == 200);
1960 }
1961 else
1962 {
1963 return false;
1964 }
1965 }
1966
1967
1968 bool OrthancPeers::DoPut(const std::string& name,
1969 const std::string& uri,
1970 const std::string& body) const
1971 {
1972 size_t index;
1973 return (LookupName(index, name) &&
1974 DoPut(index, uri, body));
1975 }
1976
1977
1978 bool OrthancPeers::DoDelete(size_t index,
1979 const std::string& uri) const
1980 {
1981 if (index >= index_.size())
1982 {
1983 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
1984 }
1985
1986 OrthancPlugins::MemoryBuffer answer;
1987 uint16_t status;
1988 OrthancPluginErrorCode code = OrthancPluginCallPeerApi
1989 (GetGlobalContext(), *answer, NULL, &status, peers_,
1990 static_cast<uint32_t>(index), OrthancPluginHttpMethod_Delete, uri.c_str(),
1991 0, NULL, NULL, NULL, 0, timeout_);
1992
1993 if (code == OrthancPluginErrorCode_Success)
1994 {
1995 return (status == 200);
1996 }
1997 else
1998 {
1999 return false;
2000 }
2001 }
2002
2003
2004 bool OrthancPeers::DoDelete(const std::string& name,
2005 const std::string& uri) const
2006 {
2007 size_t index;
2008 return (LookupName(index, name) &&
2009 DoDelete(index, uri));
2010 }
2011 #endif
2012
2013
2014
2015
2016
2017 /******************************************************************
2018 ** JOBS
2019 ******************************************************************/
2020
2021 #if HAS_ORTHANC_PLUGIN_JOB == 1
2022 void OrthancJob::CallbackFinalize(void* job)
2023 {
2024 if (job != NULL)
2025 {
2026 delete reinterpret_cast<OrthancJob*>(job);
2027 }
2028 }
2029
2030
2031 float OrthancJob::CallbackGetProgress(void* job)
2032 {
2033 assert(job != NULL);
2034
2035 try
2036 {
2037 return reinterpret_cast<OrthancJob*>(job)->progress_;
2038 }
2039 catch (...)
2040 {
2041 return 0;
2042 }
2043 }
2044
2045
2046 const char* OrthancJob::CallbackGetContent(void* job)
2047 {
2048 assert(job != NULL);
2049
2050 try
2051 {
2052 return reinterpret_cast<OrthancJob*>(job)->content_.c_str();
2053 }
2054 catch (...)
2055 {
2056 return 0;
2057 }
2058 }
2059
2060
2061 const char* OrthancJob::CallbackGetSerialized(void* job)
2062 {
2063 assert(job != NULL);
2064
2065 try
2066 {
2067 const OrthancJob& tmp = *reinterpret_cast<OrthancJob*>(job);
2068
2069 if (tmp.hasSerialized_)
2070 {
2071 return tmp.serialized_.c_str();
2072 }
2073 else
2074 {
2075 return NULL;
2076 }
2077 }
2078 catch (...)
2079 {
2080 return 0;
2081 }
2082 }
2083
2084
2085 OrthancPluginJobStepStatus OrthancJob::CallbackStep(void* job)
2086 {
2087 assert(job != NULL);
2088
2089 try
2090 {
2091 return reinterpret_cast<OrthancJob*>(job)->Step();
2092 }
2093 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&)
2094 {
2095 return OrthancPluginJobStepStatus_Failure;
2096 }
2097 catch (...)
2098 {
2099 return OrthancPluginJobStepStatus_Failure;
2100 }
2101 }
2102
2103
2104 OrthancPluginErrorCode OrthancJob::CallbackStop(void* job,
2105 OrthancPluginJobStopReason reason)
2106 {
2107 assert(job != NULL);
2108
2109 try
2110 {
2111 reinterpret_cast<OrthancJob*>(job)->Stop(reason);
2112 return OrthancPluginErrorCode_Success;
2113 }
2114 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
2115 {
2116 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
2117 }
2118 catch (...)
2119 {
2120 return OrthancPluginErrorCode_Plugin;
2121 }
2122 }
2123
2124
2125 OrthancPluginErrorCode OrthancJob::CallbackReset(void* job)
2126 {
2127 assert(job != NULL);
2128
2129 try
2130 {
2131 reinterpret_cast<OrthancJob*>(job)->Reset();
2132 return OrthancPluginErrorCode_Success;
2133 }
2134 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
2135 {
2136 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
2137 }
2138 catch (...)
2139 {
2140 return OrthancPluginErrorCode_Plugin;
2141 }
2142 }
2143
2144
2145 void OrthancJob::ClearContent()
2146 {
2147 Json::Value empty = Json::objectValue;
2148 UpdateContent(empty);
2149 }
2150
2151
2152 void OrthancJob::UpdateContent(const Json::Value& content)
2153 {
2154 if (content.type() != Json::objectValue)
2155 {
2156 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
2157 }
2158 else
2159 {
2160 WriteFastJson(content_, content);
2161 }
2162 }
2163
2164
2165 void OrthancJob::ClearSerialized()
2166 {
2167 hasSerialized_ = false;
2168 serialized_.clear();
2169 }
2170
2171
2172 void OrthancJob::UpdateSerialized(const Json::Value& serialized)
2173 {
2174 if (serialized.type() != Json::objectValue)
2175 {
2176 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
2177 }
2178 else
2179 {
2180 WriteFastJson(serialized_, serialized);
2181 hasSerialized_ = true;
2182 }
2183 }
2184
2185
2186 void OrthancJob::UpdateProgress(float progress)
2187 {
2188 if (progress < 0 ||
2189 progress > 1)
2190 {
2191 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
2192 }
2193
2194 progress_ = progress;
2195 }
2196
2197
2198 OrthancJob::OrthancJob(const std::string& jobType) :
2199 jobType_(jobType),
2200 progress_(0)
2201 {
2202 ClearContent();
2203 ClearSerialized();
2204 }
2205
2206
2207 OrthancPluginJob* OrthancJob::Create(OrthancJob* job)
2208 {
2209 if (job == NULL)
2210 {
2211 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
2212 }
2213
2214 OrthancPluginJob* orthanc = OrthancPluginCreateJob(
2215 GetGlobalContext(), job, CallbackFinalize, job->jobType_.c_str(),
2216 CallbackGetProgress, CallbackGetContent, CallbackGetSerialized,
2217 CallbackStep, CallbackStop, CallbackReset);
2218
2219 if (orthanc == NULL)
2220 {
2221 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
2222 }
2223 else
2224 {
2225 return orthanc;
2226 }
2227 }
2228
2229
2230 std::string OrthancJob::Submit(OrthancJob* job,
2231 int priority)
2232 {
2233 if (job == NULL)
2234 {
2235 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
2236 }
2237
2238 OrthancPluginJob* orthanc = Create(job);
2239
2240 char* id = OrthancPluginSubmitJob(GetGlobalContext(), orthanc, priority);
2241
2242 if (id == NULL)
2243 {
2244 LogError("Plugin cannot submit job");
2245 OrthancPluginFreeJob(GetGlobalContext(), orthanc);
2246 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
2247 }
2248 else
2249 {
2250 std::string tmp(id);
2251 tmp.assign(id);
2252 OrthancPluginFreeString(GetGlobalContext(), id);
2253
2254 return tmp;
2255 }
2256 }
2257
2258
2259 void OrthancJob::SubmitAndWait(Json::Value& result,
2260 OrthancJob* job /* takes ownership */,
2261 int priority)
2262 {
2263 std::string id = Submit(job, priority);
2264
2265 for (;;)
2266 {
2267 boost::this_thread::sleep(boost::posix_time::milliseconds(100));
2268
2269 Json::Value status;
2270 if (!RestApiGet(status, "/jobs/" + id, false) ||
2271 !status.isMember("State") ||
2272 status["State"].type() != Json::stringValue)
2273 {
2274 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InexistentItem);
2275 }
2276
2277 const std::string state = status["State"].asString();
2278 if (state == "Success")
2279 {
2280 if (status.isMember("Content"))
2281 {
2282 result = status["Content"];
2283 }
2284 else
2285 {
2286 result = Json::objectValue;
2287 }
2288
2289 return;
2290 }
2291 else if (state == "Running")
2292 {
2293 continue;
2294 }
2295 else if (!status.isMember("ErrorCode") ||
2296 status["ErrorCode"].type() != Json::intValue)
2297 {
2298 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InternalError);
2299 }
2300 else
2301 {
2302 if (!status.isMember("ErrorDescription") ||
2303 status["ErrorDescription"].type() != Json::stringValue)
2304 {
2305 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());
2306 }
2307 else
2308 {
2309 #if HAS_ORTHANC_EXCEPTION == 1
2310 throw Orthanc::OrthancException(static_cast<Orthanc::ErrorCode>(status["ErrorCode"].asInt()),
2311 status["ErrorDescription"].asString());
2312 #else
2313 LogError("Exception while executing the job: " + status["ErrorDescription"].asString());
2314 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());
2315 #endif
2316 }
2317 }
2318 }
2319 }
2320
2321
2322 void OrthancJob::SubmitFromRestApiPost(OrthancPluginRestOutput* output,
2323 const Json::Value& body,
2324 OrthancJob* job)
2325 {
2326 static const char* KEY_SYNCHRONOUS = "Synchronous";
2327 static const char* KEY_ASYNCHRONOUS = "Asynchronous";
2328 static const char* KEY_PRIORITY = "Priority";
2329
2330 boost::movelib::unique_ptr<OrthancJob> protection(job);
2331
2332 if (body.type() != Json::objectValue)
2333 {
2334 #if HAS_ORTHANC_EXCEPTION == 1
2335 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
2336 "Expected a JSON object in the body");
2337 #else
2338 LogError("Expected a JSON object in the body");
2339 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
2340 #endif
2341 }
2342
2343 bool synchronous = true;
2344
2345 if (body.isMember(KEY_SYNCHRONOUS))
2346 {
2347 if (body[KEY_SYNCHRONOUS].type() != Json::booleanValue)
2348 {
2349 #if HAS_ORTHANC_EXCEPTION == 1
2350 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
2351 "Option \"" + std::string(KEY_SYNCHRONOUS) +
2352 "\" must be Boolean");
2353 #else
2354 LogError("Option \"" + std::string(KEY_SYNCHRONOUS) + "\" must be Boolean");
2355 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
2356 #endif
2357 }
2358 else
2359 {
2360 synchronous = body[KEY_SYNCHRONOUS].asBool();
2361 }
2362 }
2363
2364 if (body.isMember(KEY_ASYNCHRONOUS))
2365 {
2366 if (body[KEY_ASYNCHRONOUS].type() != Json::booleanValue)
2367 {
2368 #if HAS_ORTHANC_EXCEPTION == 1
2369 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
2370 "Option \"" + std::string(KEY_ASYNCHRONOUS) +
2371 "\" must be Boolean");
2372 #else
2373 LogError("Option \"" + std::string(KEY_ASYNCHRONOUS) + "\" must be Boolean");
2374 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
2375 #endif
2376 }
2377 else
2378 {
2379 synchronous = !body[KEY_ASYNCHRONOUS].asBool();
2380 }
2381 }
2382
2383 int priority = 0;
2384
2385 if (body.isMember(KEY_PRIORITY))
2386 {
2387 if (body[KEY_PRIORITY].type() != Json::booleanValue)
2388 {
2389 #if HAS_ORTHANC_EXCEPTION == 1
2390 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
2391 "Option \"" + std::string(KEY_PRIORITY) +
2392 "\" must be an integer");
2393 #else
2394 LogError("Option \"" + std::string(KEY_PRIORITY) + "\" must be an integer");
2395 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
2396 #endif
2397 }
2398 else
2399 {
2400 priority = !body[KEY_PRIORITY].asInt();
2401 }
2402 }
2403
2404 Json::Value result;
2405
2406 if (synchronous)
2407 {
2408 OrthancPlugins::OrthancJob::SubmitAndWait(result, protection.release(), priority);
2409 }
2410 else
2411 {
2412 std::string id = OrthancPlugins::OrthancJob::Submit(protection.release(), priority);
2413
2414 result = Json::objectValue;
2415 result["ID"] = id;
2416 result["Path"] = "/jobs/" + id;
2417 }
2418
2419 std::string s = result.toStyledString();
2420 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(),
2421 s.size(), "application/json");
2422 }
2423
2424 #endif
2425
2426
2427
2428
2429 /******************************************************************
2430 ** METRICS
2431 ******************************************************************/
2432
2433 #if HAS_ORTHANC_PLUGIN_METRICS == 1
2434 MetricsTimer::MetricsTimer(const char* name) :
2435 name_(name)
2436 {
2437 start_ = boost::posix_time::microsec_clock::universal_time();
2438 }
2439
2440 MetricsTimer::~MetricsTimer()
2441 {
2442 const boost::posix_time::ptime stop = boost::posix_time::microsec_clock::universal_time();
2443 const boost::posix_time::time_duration diff = stop - start_;
2444 OrthancPluginSetMetricsValue(GetGlobalContext(), name_.c_str(), static_cast<float>(diff.total_milliseconds()),
2445 OrthancPluginMetricsType_Timer);
2446 }
2447 #endif
2448
2449
2450
2451
2452 /******************************************************************
2453 ** HTTP CLIENT
2454 ******************************************************************/
2455
2456 #if HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1
2457 class HttpClient::RequestBodyWrapper : public boost::noncopyable
2458 {
2459 private:
2460 static RequestBodyWrapper& GetObject(void* body)
2461 {
2462 assert(body != NULL);
2463 return *reinterpret_cast<RequestBodyWrapper*>(body);
2464 }
2465
2466 IRequestBody& body_;
2467 bool done_;
2468 std::string chunk_;
2469
2470 public:
2471 RequestBodyWrapper(IRequestBody& body) :
2472 body_(body),
2473 done_(false)
2474 {
2475 }
2476
2477 static uint8_t IsDone(void* body)
2478 {
2479 return GetObject(body).done_;
2480 }
2481
2482 static const void* GetChunkData(void* body)
2483 {
2484 return GetObject(body).chunk_.c_str();
2485 }
2486
2487 static uint32_t GetChunkSize(void* body)
2488 {
2489 return static_cast<uint32_t>(GetObject(body).chunk_.size());
2490 }
2491
2492 static OrthancPluginErrorCode Next(void* body)
2493 {
2494 RequestBodyWrapper& that = GetObject(body);
2495
2496 if (that.done_)
2497 {
2498 return OrthancPluginErrorCode_BadSequenceOfCalls;
2499 }
2500 else
2501 {
2502 try
2503 {
2504 that.done_ = !that.body_.ReadNextChunk(that.chunk_);
2505 return OrthancPluginErrorCode_Success;
2506 }
2507 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
2508 {
2509 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
2510 }
2511 catch (...)
2512 {
2513 return OrthancPluginErrorCode_InternalError;
2514 }
2515 }
2516 }
2517 };
2518
2519
2520 #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
2521 static OrthancPluginErrorCode AnswerAddHeaderCallback(void* answer,
2522 const char* key,
2523 const char* value)
2524 {
2525 assert(answer != NULL && key != NULL && value != NULL);
2526
2527 try
2528 {
2529 reinterpret_cast<HttpClient::IAnswer*>(answer)->AddHeader(key, value);
2530 return OrthancPluginErrorCode_Success;
2531 }
2532 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
2533 {
2534 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
2535 }
2536 catch (...)
2537 {
2538 return OrthancPluginErrorCode_Plugin;
2539 }
2540 }
2541 #endif
2542
2543
2544 #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
2545 static OrthancPluginErrorCode AnswerAddChunkCallback(void* answer,
2546 const void* data,
2547 uint32_t size)
2548 {
2549 assert(answer != NULL);
2550
2551 try
2552 {
2553 reinterpret_cast<HttpClient::IAnswer*>(answer)->AddChunk(data, size);
2554 return OrthancPluginErrorCode_Success;
2555 }
2556 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
2557 {
2558 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
2559 }
2560 catch (...)
2561 {
2562 return OrthancPluginErrorCode_Plugin;
2563 }
2564 }
2565 #endif
2566
2567
2568 HttpClient::HttpClient() :
2569 httpStatus_(0),
2570 method_(OrthancPluginHttpMethod_Get),
2571 timeout_(0),
2572 pkcs11_(false),
2573 chunkedBody_(NULL),
2574 allowChunkedTransfers_(true)
2575 {
2576 }
2577
2578
2579 void HttpClient::AddHeaders(const HttpHeaders& headers)
2580 {
2581 for (HttpHeaders::const_iterator it = headers.begin();
2582 it != headers.end(); ++it)
2583 {
2584 headers_[it->first] = it->second;
2585 }
2586 }
2587
2588
2589 void HttpClient::SetCredentials(const std::string& username,
2590 const std::string& password)
2591 {
2592 username_ = username;
2593 password_ = password;
2594 }
2595
2596
2597 void HttpClient::ClearCredentials()
2598 {
2599 username_.clear();
2600 password_.clear();
2601 }
2602
2603
2604 void HttpClient::SetCertificate(const std::string& certificateFile,
2605 const std::string& keyFile,
2606 const std::string& keyPassword)
2607 {
2608 certificateFile_ = certificateFile;
2609 certificateKeyFile_ = keyFile;
2610 certificateKeyPassword_ = keyPassword;
2611 }
2612
2613
2614 void HttpClient::ClearCertificate()
2615 {
2616 certificateFile_.clear();
2617 certificateKeyFile_.clear();
2618 certificateKeyPassword_.clear();
2619 }
2620
2621
2622 void HttpClient::ClearBody()
2623 {
2624 fullBody_.clear();
2625 chunkedBody_ = NULL;
2626 }
2627
2628
2629 void HttpClient::SwapBody(std::string& body)
2630 {
2631 fullBody_.swap(body);
2632 chunkedBody_ = NULL;
2633 }
2634
2635
2636 void HttpClient::SetBody(const std::string& body)
2637 {
2638 fullBody_ = body;
2639 chunkedBody_ = NULL;
2640 }
2641
2642
2643 void HttpClient::SetBody(IRequestBody& body)
2644 {
2645 fullBody_.clear();
2646 chunkedBody_ = &body;
2647 }
2648
2649
2650 namespace
2651 {
2652 class HeadersWrapper : public boost::noncopyable
2653 {
2654 private:
2655 std::vector<const char*> headersKeys_;
2656 std::vector<const char*> headersValues_;
2657
2658 public:
2659 HeadersWrapper(const HttpClient::HttpHeaders& headers)
2660 {
2661 headersKeys_.reserve(headers.size());
2662 headersValues_.reserve(headers.size());
2663
2664 for (HttpClient::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); ++it)
2665 {
2666 headersKeys_.push_back(it->first.c_str());
2667 headersValues_.push_back(it->second.c_str());
2668 }
2669 }
2670
2671 void AddStaticString(const char* key,
2672 const char* value)
2673 {
2674 headersKeys_.push_back(key);
2675 headersValues_.push_back(value);
2676 }
2677
2678 uint32_t GetCount() const
2679 {
2680 return headersKeys_.size();
2681 }
2682
2683 const char* const* GetKeys() const
2684 {
2685 return headersKeys_.empty() ? NULL : &headersKeys_[0];
2686 }
2687
2688 const char* const* GetValues() const
2689 {
2690 return headersValues_.empty() ? NULL : &headersValues_[0];
2691 }
2692 };
2693
2694
2695 class MemoryRequestBody : public HttpClient::IRequestBody
2696 {
2697 private:
2698 std::string body_;
2699 bool done_;
2700
2701 public:
2702 MemoryRequestBody(const std::string& body) :
2703 body_(body),
2704 done_(false)
2705 {
2706 if (body_.empty())
2707 {
2708 done_ = true;
2709 }
2710 }
2711
2712 virtual bool ReadNextChunk(std::string& chunk)
2713 {
2714 if (done_)
2715 {
2716 return false;
2717 }
2718 else
2719 {
2720 chunk.swap(body_);
2721 done_ = true;
2722 return true;
2723 }
2724 }
2725 };
2726
2727
2728 // This class mimics Orthanc::ChunkedBuffer
2729 class ChunkedBuffer : public boost::noncopyable
2730 {
2731 private:
2732 typedef std::list<std::string*> Content;
2733
2734 Content content_;
2735 size_t size_;
2736
2737 public:
2738 ChunkedBuffer() :
2739 size_(0)
2740 {
2741 }
2742
2743 ~ChunkedBuffer()
2744 {
2745 Clear();
2746 }
2747
2748 void Clear()
2749 {
2750 for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
2751 {
2752 assert(*it != NULL);
2753 delete *it;
2754 }
2755
2756 size_ = 0;
2757 content_.clear();
2758 }
2759
2760 /**
2761 * Since Orthanc 1.9.3, this function also clears the content of
2762 * the ChunkedBuffer in order to mimic the behavior of the
2763 * original class "Orthanc::ChunkedBuffer". This prevents the
2764 * forgetting of calling "Clear()" in order to reduce memory
2765 * consumption.
2766 **/
2767 void Flatten(std::string& target)
2768 {
2769 target.resize(size_);
2770
2771 size_t pos = 0;
2772
2773 for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it)
2774 {
2775 assert(*it != NULL);
2776 size_t s = (*it)->size();
2777
2778 if (s != 0)
2779 {
2780 memcpy(&target[pos], (*it)->c_str(), s);
2781 pos += s;
2782 }
2783
2784 delete *it;
2785 }
2786
2787 assert(pos == target.size());
2788
2789 size_ = 0;
2790 content_.clear();
2791 }
2792
2793 void AddChunk(const void* data,
2794 size_t size)
2795 {
2796 content_.push_back(new std::string(reinterpret_cast<const char*>(data), size));
2797 size_ += size;
2798 }
2799
2800 void AddChunk(const std::string& chunk)
2801 {
2802 content_.push_back(new std::string(chunk));
2803 size_ += chunk.size();
2804 }
2805 };
2806
2807
2808 #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
2809 class MemoryAnswer : public HttpClient::IAnswer
2810 {
2811 private:
2812 HttpClient::HttpHeaders headers_;
2813 ChunkedBuffer body_;
2814
2815 public:
2816 const HttpClient::HttpHeaders& GetHeaders() const
2817 {
2818 return headers_;
2819 }
2820
2821 ChunkedBuffer& GetBody()
2822 {
2823 return body_;
2824 }
2825
2826 virtual void AddHeader(const std::string& key,
2827 const std::string& value)
2828 {
2829 headers_[key] = value;
2830 }
2831
2832 virtual void AddChunk(const void* data,
2833 size_t size)
2834 {
2835 body_.AddChunk(data, size);
2836 }
2837 };
2838 #endif
2839 }
2840
2841
2842 #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
2843 void HttpClient::ExecuteWithStream(uint16_t& httpStatus,
2844 IAnswer& answer,
2845 IRequestBody& body) const
2846 {
2847 HeadersWrapper h(headers_);
2848
2849 if (method_ == OrthancPluginHttpMethod_Post ||
2850 method_ == OrthancPluginHttpMethod_Put)
2851 {
2852 // Automatically set the "Transfer-Encoding" header if absent
2853 bool found = false;
2854
2855 for (HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); ++it)
2856 {
2857 if (boost::iequals(it->first, "Transfer-Encoding"))
2858 {
2859 found = true;
2860 break;
2861 }
2862 }
2863
2864 if (!found)
2865 {
2866 h.AddStaticString("Transfer-Encoding", "chunked");
2867 }
2868 }
2869
2870 RequestBodyWrapper request(body);
2871
2872 OrthancPluginErrorCode error = OrthancPluginChunkedHttpClient(
2873 GetGlobalContext(),
2874 &answer,
2875 AnswerAddChunkCallback,
2876 AnswerAddHeaderCallback,
2877 &httpStatus,
2878 method_,
2879 url_.c_str(),
2880 h.GetCount(),
2881 h.GetKeys(),
2882 h.GetValues(),
2883 &request,
2884 RequestBodyWrapper::IsDone,
2885 RequestBodyWrapper::GetChunkData,
2886 RequestBodyWrapper::GetChunkSize,
2887 RequestBodyWrapper::Next,
2888 username_.empty() ? NULL : username_.c_str(),
2889 password_.empty() ? NULL : password_.c_str(),
2890 timeout_,
2891 certificateFile_.empty() ? NULL : certificateFile_.c_str(),
2892 certificateFile_.empty() ? NULL : certificateKeyFile_.c_str(),
2893 certificateFile_.empty() ? NULL : certificateKeyPassword_.c_str(),
2894 pkcs11_ ? 1 : 0);
2895
2896 if (error != OrthancPluginErrorCode_Success)
2897 {
2898 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
2899 }
2900 }
2901 #endif
2902
2903
2904 void HttpClient::ExecuteWithoutStream(uint16_t& httpStatus,
2905 HttpHeaders& answerHeaders,
2906 std::string& answerBody,
2907 const std::string& body) const
2908 {
2909 HeadersWrapper headers(headers_);
2910
2911 MemoryBuffer answerBodyBuffer, answerHeadersBuffer;
2912
2913 if (body.size() > 0xffffffffu)
2914 {
2915 LogError("Cannot handle body size > 4GB");
2916 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
2917 }
2918
2919 OrthancPluginErrorCode error = OrthancPluginHttpClient(
2920 GetGlobalContext(),
2921 *answerBodyBuffer,
2922 *answerHeadersBuffer,
2923 &httpStatus,
2924 method_,
2925 url_.c_str(),
2926 headers.GetCount(),
2927 headers.GetKeys(),
2928 headers.GetValues(),
2929 body.empty() ? NULL : body.c_str(),
2930 body.size(),
2931 username_.empty() ? NULL : username_.c_str(),
2932 password_.empty() ? NULL : password_.c_str(),
2933 timeout_,
2934 certificateFile_.empty() ? NULL : certificateFile_.c_str(),
2935 certificateFile_.empty() ? NULL : certificateKeyFile_.c_str(),
2936 certificateFile_.empty() ? NULL : certificateKeyPassword_.c_str(),
2937 pkcs11_ ? 1 : 0);
2938
2939 if (error != OrthancPluginErrorCode_Success)
2940 {
2941 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
2942 }
2943
2944 Json::Value v;
2945 answerHeadersBuffer.ToJson(v);
2946
2947 if (v.type() != Json::objectValue)
2948 {
2949 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
2950 }
2951
2952 Json::Value::Members members = v.getMemberNames();
2953 answerHeaders.clear();
2954
2955 for (size_t i = 0; i < members.size(); i++)
2956 {
2957 const Json::Value& h = v[members[i]];
2958 if (h.type() != Json::stringValue)
2959 {
2960 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
2961 }
2962 else
2963 {
2964 answerHeaders[members[i]] = h.asString();
2965 }
2966 }
2967
2968 answerBodyBuffer.ToString(answerBody);
2969 }
2970
2971
2972 void HttpClient::Execute(IAnswer& answer)
2973 {
2974 #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
2975 if (allowChunkedTransfers_)
2976 {
2977 if (chunkedBody_ != NULL)
2978 {
2979 ExecuteWithStream(httpStatus_, answer, *chunkedBody_);
2980 }
2981 else
2982 {
2983 MemoryRequestBody wrapper(fullBody_);
2984 ExecuteWithStream(httpStatus_, answer, wrapper);
2985 }
2986
2987 return;
2988 }
2989 #endif
2990
2991 // Compatibility mode for Orthanc SDK <= 1.5.6 or if chunked
2992 // transfers are disabled. This results in higher memory usage
2993 // (all chunks from the answer body are sent at once)
2994
2995 HttpHeaders answerHeaders;
2996 std::string answerBody;
2997 Execute(answerHeaders, answerBody);
2998
2999 for (HttpHeaders::const_iterator it = answerHeaders.begin();
3000 it != answerHeaders.end(); ++it)
3001 {
3002 answer.AddHeader(it->first, it->second);
3003 }
3004
3005 if (!answerBody.empty())
3006 {
3007 answer.AddChunk(answerBody.c_str(), answerBody.size());
3008 }
3009 }
3010
3011
3012 void HttpClient::Execute(HttpHeaders& answerHeaders /* out */,
3013 std::string& answerBody /* out */)
3014 {
3015 #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
3016 if (allowChunkedTransfers_)
3017 {
3018 MemoryAnswer answer;
3019 Execute(answer);
3020 answerHeaders = answer.GetHeaders();
3021 answer.GetBody().Flatten(answerBody);
3022 return;
3023 }
3024 #endif
3025
3026 // Compatibility mode for Orthanc SDK <= 1.5.6 or if chunked
3027 // transfers are disabled. This results in higher memory usage
3028 // (all chunks from the request body are sent at once)
3029
3030 if (chunkedBody_ != NULL)
3031 {
3032 ChunkedBuffer buffer;
3033
3034 std::string chunk;
3035 while (chunkedBody_->ReadNextChunk(chunk))
3036 {
3037 buffer.AddChunk(chunk);
3038 }
3039
3040 std::string body;
3041 buffer.Flatten(body);
3042
3043 ExecuteWithoutStream(httpStatus_, answerHeaders, answerBody, body);
3044 }
3045 else
3046 {
3047 ExecuteWithoutStream(httpStatus_, answerHeaders, answerBody, fullBody_);
3048 }
3049 }
3050
3051
3052 void HttpClient::Execute(HttpHeaders& answerHeaders /* out */,
3053 Json::Value& answerBody /* out */)
3054 {
3055 std::string body;
3056 Execute(answerHeaders, body);
3057
3058 if (!ReadJson(answerBody, body))
3059 {
3060 LogError("Cannot convert HTTP answer body to JSON");
3061 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
3062 }
3063 }
3064
3065
3066 void HttpClient::Execute()
3067 {
3068 HttpHeaders answerHeaders;
3069 std::string body;
3070 Execute(answerHeaders, body);
3071 }
3072
3073 #endif /* HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1 */
3074
3075
3076
3077
3078
3079 /******************************************************************
3080 ** CHUNKED HTTP SERVER
3081 ******************************************************************/
3082
3083 namespace Internals
3084 {
3085 void NullRestCallback(OrthancPluginRestOutput* output,
3086 const char* url,
3087 const OrthancPluginHttpRequest* request)
3088 {
3089 }
3090
3091 IChunkedRequestReader *NullChunkedRestCallback(const char* url,
3092 const OrthancPluginHttpRequest* request)
3093 {
3094 return NULL;
3095 }
3096
3097
3098 #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
3099
3100 OrthancPluginErrorCode ChunkedRequestReaderAddChunk(
3101 OrthancPluginServerChunkedRequestReader* reader,
3102 const void* data,
3103 uint32_t size)
3104 {
3105 try
3106 {
3107 if (reader == NULL)
3108 {
3109 return OrthancPluginErrorCode_InternalError;
3110 }
3111
3112 reinterpret_cast<IChunkedRequestReader*>(reader)->AddChunk(data, size);
3113 return OrthancPluginErrorCode_Success;
3114 }
3115 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
3116 {
3117 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
3118 }
3119 catch (boost::bad_lexical_cast&)
3120 {
3121 return OrthancPluginErrorCode_BadFileFormat;
3122 }
3123 catch (...)
3124 {
3125 return OrthancPluginErrorCode_Plugin;
3126 }
3127 }
3128
3129
3130 OrthancPluginErrorCode ChunkedRequestReaderExecute(
3131 OrthancPluginServerChunkedRequestReader* reader,
3132 OrthancPluginRestOutput* output)
3133 {
3134 try
3135 {
3136 if (reader == NULL)
3137 {
3138 return OrthancPluginErrorCode_InternalError;
3139 }
3140
3141 reinterpret_cast<IChunkedRequestReader*>(reader)->Execute(output);
3142 return OrthancPluginErrorCode_Success;
3143 }
3144 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
3145 {
3146 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
3147 }
3148 catch (boost::bad_lexical_cast&)
3149 {
3150 return OrthancPluginErrorCode_BadFileFormat;
3151 }
3152 catch (...)
3153 {
3154 return OrthancPluginErrorCode_Plugin;
3155 }
3156 }
3157
3158
3159 void ChunkedRequestReaderFinalize(
3160 OrthancPluginServerChunkedRequestReader* reader)
3161 {
3162 if (reader != NULL)
3163 {
3164 delete reinterpret_cast<IChunkedRequestReader*>(reader);
3165 }
3166 }
3167
3168 #else
3169
3170 OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output,
3171 const char* url,
3172 const OrthancPluginHttpRequest* request,
3173 RestCallback GetHandler,
3174 ChunkedRestCallback PostHandler,
3175 RestCallback DeleteHandler,
3176 ChunkedRestCallback PutHandler)
3177 {
3178 try
3179 {
3180 std::string allowed;
3181
3182 if (GetHandler != Internals::NullRestCallback)
3183 {
3184 allowed += "GET";
3185 }
3186
3187 if (PostHandler != Internals::NullChunkedRestCallback)
3188 {
3189 if (!allowed.empty())
3190 {
3191 allowed += ",";
3192 }
3193
3194 allowed += "POST";
3195 }
3196
3197 if (DeleteHandler != Internals::NullRestCallback)
3198 {
3199 if (!allowed.empty())
3200 {
3201 allowed += ",";
3202 }
3203
3204 allowed += "DELETE";
3205 }
3206
3207 if (PutHandler != Internals::NullChunkedRestCallback)
3208 {
3209 if (!allowed.empty())
3210 {
3211 allowed += ",";
3212 }
3213
3214 allowed += "PUT";
3215 }
3216
3217 switch (request->method)
3218 {
3219 case OrthancPluginHttpMethod_Get:
3220 if (GetHandler == Internals::NullRestCallback)
3221 {
3222 OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
3223 }
3224 else
3225 {
3226 GetHandler(output, url, request);
3227 }
3228
3229 break;
3230
3231 case OrthancPluginHttpMethod_Post:
3232 if (PostHandler == Internals::NullChunkedRestCallback)
3233 {
3234 OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
3235 }
3236 else
3237 {
3238 boost::movelib::unique_ptr<IChunkedRequestReader> reader(PostHandler(url, request));
3239 if (reader.get() == NULL)
3240 {
3241 ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
3242 }
3243 else
3244 {
3245 reader->AddChunk(request->body, request->bodySize);
3246 reader->Execute(output);
3247 }
3248 }
3249
3250 break;
3251
3252 case OrthancPluginHttpMethod_Delete:
3253 if (DeleteHandler == Internals::NullRestCallback)
3254 {
3255 OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
3256 }
3257 else
3258 {
3259 DeleteHandler(output, url, request);
3260 }
3261
3262 break;
3263
3264 case OrthancPluginHttpMethod_Put:
3265 if (PutHandler == Internals::NullChunkedRestCallback)
3266 {
3267 OrthancPluginSendMethodNotAllowed(GetGlobalContext(), output, allowed.c_str());
3268 }
3269 else
3270 {
3271 boost::movelib::unique_ptr<IChunkedRequestReader> reader(PutHandler(url, request));
3272 if (reader.get() == NULL)
3273 {
3274 ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
3275 }
3276 else
3277 {
3278 reader->AddChunk(request->body, request->bodySize);
3279 reader->Execute(output);
3280 }
3281 }
3282
3283 break;
3284
3285 default:
3286 ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
3287 }
3288
3289 return OrthancPluginErrorCode_Success;
3290 }
3291 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
3292 {
3293 #if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1
3294 if (HasGlobalContext() &&
3295 e.HasDetails())
3296 {
3297 // The "false" instructs Orthanc not to log the detailed
3298 // error message. This is to avoid duplicating the details,
3299 // because "OrthancException" already does it on construction.
3300 OrthancPluginSetHttpErrorDetails
3301 (GetGlobalContext(), output, e.GetDetails(), false);
3302 }
3303 #endif
3304
3305 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
3306 }
3307 catch (boost::bad_lexical_cast&)
3308 {
3309 return OrthancPluginErrorCode_BadFileFormat;
3310 }
3311 catch (...)
3312 {
3313 return OrthancPluginErrorCode_Plugin;
3314 }
3315 }
3316 #endif
3317 }
3318
3319
3320 #if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
3321 OrthancPluginErrorCode IStorageCommitmentScpHandler::Lookup(
3322 OrthancPluginStorageCommitmentFailureReason* target,
3323 void* rawHandler,
3324 const char* sopClassUid,
3325 const char* sopInstanceUid)
3326 {
3327 assert(target != NULL &&
3328 rawHandler != NULL);
3329
3330 try
3331 {
3332 IStorageCommitmentScpHandler& handler = *reinterpret_cast<IStorageCommitmentScpHandler*>(rawHandler);
3333 *target = handler.Lookup(sopClassUid, sopInstanceUid);
3334 return OrthancPluginErrorCode_Success;
3335 }
3336 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
3337 {
3338 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
3339 }
3340 catch (...)
3341 {
3342 return OrthancPluginErrorCode_Plugin;
3343 }
3344 }
3345 #endif
3346
3347
3348 #if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
3349 void IStorageCommitmentScpHandler::Destructor(void* rawHandler)
3350 {
3351 assert(rawHandler != NULL);
3352 delete reinterpret_cast<IStorageCommitmentScpHandler*>(rawHandler);
3353 }
3354 #endif
3355
3356
3357 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
3358 DicomInstance::DicomInstance(const OrthancPluginDicomInstance* instance) :
3359 toFree_(false),
3360 instance_(instance)
3361 {
3362 }
3363 #else
3364 DicomInstance::DicomInstance(OrthancPluginDicomInstance* instance) :
3365 toFree_(false),
3366 instance_(instance)
3367 {
3368 }
3369 #endif
3370
3371
3372 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
3373 DicomInstance::DicomInstance(const void* buffer,
3374 size_t size) :
3375 toFree_(true),
3376 instance_(OrthancPluginCreateDicomInstance(GetGlobalContext(), buffer, size))
3377 {
3378 if (instance_ == NULL)
3379 {
3380 ORTHANC_PLUGINS_THROW_EXCEPTION(NullPointer);
3381 }
3382 }
3383 #endif
3384
3385
3386 DicomInstance::~DicomInstance()
3387 {
3388 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
3389 if (toFree_ &&
3390 instance_ != NULL)
3391 {
3392 OrthancPluginFreeDicomInstance(
3393 GetGlobalContext(), const_cast<OrthancPluginDicomInstance*>(instance_));
3394 }
3395 #endif
3396 }
3397
3398
3399 std::string DicomInstance::GetRemoteAet() const
3400 {
3401 const char* s = OrthancPluginGetInstanceRemoteAet(GetGlobalContext(), instance_);
3402 if (s == NULL)
3403 {
3404 ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
3405 }
3406 else
3407 {
3408 return std::string(s);
3409 }
3410 }
3411
3412
3413 void DicomInstance::GetJson(Json::Value& target) const
3414 {
3415 OrthancString s;
3416 s.Assign(OrthancPluginGetInstanceJson(GetGlobalContext(), instance_));
3417 s.ToJson(target);
3418 }
3419
3420
3421 void DicomInstance::GetSimplifiedJson(Json::Value& target) const
3422 {
3423 OrthancString s;
3424 s.Assign(OrthancPluginGetInstanceSimplifiedJson(GetGlobalContext(), instance_));
3425 s.ToJson(target);
3426 }
3427
3428
3429 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
3430 std::string DicomInstance::GetTransferSyntaxUid() const
3431 {
3432 OrthancString s;
3433 s.Assign(OrthancPluginGetInstanceTransferSyntaxUid(GetGlobalContext(), instance_));
3434
3435 std::string result;
3436 s.ToString(result);
3437 return result;
3438 }
3439 #endif
3440
3441
3442 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
3443 bool DicomInstance::HasPixelData() const
3444 {
3445 int32_t result = OrthancPluginHasInstancePixelData(GetGlobalContext(), instance_);
3446 if (result < 0)
3447 {
3448 ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
3449 }
3450 else
3451 {
3452 return (result != 0);
3453 }
3454 }
3455 #endif
3456
3457
3458 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
3459 void DicomInstance::GetRawFrame(std::string& target,
3460 unsigned int frameIndex) const
3461 {
3462 MemoryBuffer buffer;
3463 OrthancPluginErrorCode code = OrthancPluginGetInstanceRawFrame(
3464 GetGlobalContext(), *buffer, instance_, frameIndex);
3465
3466 if (code == OrthancPluginErrorCode_Success)
3467 {
3468 buffer.ToString(target);
3469 }
3470 else
3471 {
3472 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
3473 }
3474 }
3475 #endif
3476
3477
3478 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
3479 OrthancImage* DicomInstance::GetDecodedFrame(unsigned int frameIndex) const
3480 {
3481 OrthancPluginImage* image = OrthancPluginGetInstanceDecodedFrame(
3482 GetGlobalContext(), instance_, frameIndex);
3483
3484 if (image == NULL)
3485 {
3486 ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
3487 }
3488 else
3489 {
3490 return new OrthancImage(image);
3491 }
3492 }
3493 #endif
3494
3495
3496 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
3497 void DicomInstance::Serialize(std::string& target) const
3498 {
3499 MemoryBuffer buffer;
3500 OrthancPluginErrorCode code = OrthancPluginSerializeDicomInstance(
3501 GetGlobalContext(), *buffer, instance_);
3502
3503 if (code == OrthancPluginErrorCode_Success)
3504 {
3505 buffer.ToString(target);
3506 }
3507 else
3508 {
3509 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
3510 }
3511 }
3512 #endif
3513
3514
3515 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
3516 DicomInstance* DicomInstance::Transcode(const void* buffer,
3517 size_t size,
3518 const std::string& transferSyntax)
3519 {
3520 OrthancPluginDicomInstance* instance = OrthancPluginTranscodeDicomInstance(
3521 GetGlobalContext(), buffer, size, transferSyntax.c_str());
3522
3523 if (instance == NULL)
3524 {
3525 ORTHANC_PLUGINS_THROW_EXCEPTION(Plugin);
3526 }
3527 else
3528 {
3529 boost::movelib::unique_ptr<DicomInstance> result(new DicomInstance(instance));
3530 result->toFree_ = true;
3531 return result.release();
3532 }
3533 }
3534 #endif
3535 }