comparison Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp @ 0:2464bacb730b

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