comparison RenderingPlugin/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h @ 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.h@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 #pragma once
24
25 #include "OrthancPluginException.h"
26
27 #include <orthanc/OrthancCPlugin.h>
28 #include <boost/noncopyable.hpp>
29 #include <boost/lexical_cast.hpp>
30 #include <boost/date_time/posix_time/posix_time.hpp>
31 #include <json/value.h>
32 #include <vector>
33 #include <list>
34 #include <set>
35 #include <map>
36
37
38
39 /**
40 * The definition of ORTHANC_PLUGINS_VERSION_IS_ABOVE below is for
41 * backward compatibility with Orthanc SDK <= 1.3.0.
42 *
43 * $ hg diff -r Orthanc-1.3.0:Orthanc-1.3.1 ../../../Plugins/Include/orthanc/OrthancCPlugin.h
44 *
45 **/
46 #if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE)
47 #define ORTHANC_PLUGINS_VERSION_IS_ABOVE(major, minor, revision) \
48 (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER > major || \
49 (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER == major && \
50 (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER > minor || \
51 (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER == minor && \
52 ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= revision))))
53 #endif
54
55
56 #if !defined(ORTHANC_FRAMEWORK_VERSION_IS_ABOVE)
57 #define ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(major, minor, revision) \
58 (ORTHANC_VERSION_MAJOR > major || \
59 (ORTHANC_VERSION_MAJOR == major && \
60 (ORTHANC_VERSION_MINOR > minor || \
61 (ORTHANC_VERSION_MINOR == minor && \
62 ORTHANC_VERSION_REVISION >= revision))))
63 #endif
64
65
66 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 0)
67 // The "OrthancPluginFindMatcher()" primitive was introduced in Orthanc 1.2.0
68 # define HAS_ORTHANC_PLUGIN_FIND_MATCHER 1
69 #else
70 # define HAS_ORTHANC_PLUGIN_FIND_MATCHER 0
71 #endif
72
73
74 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 4, 2)
75 # define HAS_ORTHANC_PLUGIN_PEERS 1
76 # define HAS_ORTHANC_PLUGIN_JOB 1
77 #else
78 # define HAS_ORTHANC_PLUGIN_PEERS 0
79 # define HAS_ORTHANC_PLUGIN_JOB 0
80 #endif
81
82 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
83 # define HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS 1
84 #else
85 # define HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS 0
86 #endif
87
88 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4)
89 # define HAS_ORTHANC_PLUGIN_METRICS 1
90 #else
91 # define HAS_ORTHANC_PLUGIN_METRICS 0
92 #endif
93
94 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 1, 0)
95 # define HAS_ORTHANC_PLUGIN_HTTP_CLIENT 1
96 #else
97 # define HAS_ORTHANC_PLUGIN_HTTP_CLIENT 0
98 #endif
99
100 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7)
101 # define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT 1
102 #else
103 # define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT 0
104 #endif
105
106 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7)
107 # define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER 1
108 #else
109 # define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER 0
110 #endif
111
112 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 0)
113 # define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP 1
114 #else
115 # define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP 0
116 #endif
117
118
119
120 namespace OrthancPlugins
121 {
122 typedef void (*RestCallback) (OrthancPluginRestOutput* output,
123 const char* url,
124 const OrthancPluginHttpRequest* request);
125
126 void SetGlobalContext(OrthancPluginContext* context);
127
128 bool HasGlobalContext();
129
130 OrthancPluginContext* GetGlobalContext();
131
132
133 class OrthancImage;
134
135
136 class MemoryBuffer : public boost::noncopyable
137 {
138 private:
139 OrthancPluginMemoryBuffer buffer_;
140
141 void Check(OrthancPluginErrorCode code);
142
143 bool CheckHttp(OrthancPluginErrorCode code);
144
145 public:
146 MemoryBuffer();
147
148 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
149 // This constructor makes a copy of the given buffer in the memory
150 // handled by the Orthanc core
151 MemoryBuffer(const void* buffer,
152 size_t size);
153 #endif
154
155 ~MemoryBuffer()
156 {
157 Clear();
158 }
159
160 OrthancPluginMemoryBuffer* operator*()
161 {
162 return &buffer_;
163 }
164
165 // This transfers ownership from "other" to "this"
166 void Assign(OrthancPluginMemoryBuffer& other);
167
168 void Swap(MemoryBuffer& other);
169
170 OrthancPluginMemoryBuffer Release();
171
172 const char* GetData() const
173 {
174 if (buffer_.size > 0)
175 {
176 return reinterpret_cast<const char*>(buffer_.data);
177 }
178 else
179 {
180 return NULL;
181 }
182 }
183
184 size_t GetSize() const
185 {
186 return buffer_.size;
187 }
188
189 bool IsEmpty() const
190 {
191 return GetSize() == 0 || GetData() == NULL;
192 }
193
194 void Clear();
195
196 void ToString(std::string& target) const;
197
198 void ToJson(Json::Value& target) const;
199
200 bool RestApiGet(const std::string& uri,
201 bool applyPlugins);
202
203 bool RestApiGet(const std::string& uri,
204 const std::map<std::string, std::string>& httpHeaders,
205 bool applyPlugins);
206
207 bool RestApiPost(const std::string& uri,
208 const void* body,
209 size_t bodySize,
210 bool applyPlugins);
211
212 bool RestApiPut(const std::string& uri,
213 const void* body,
214 size_t bodySize,
215 bool applyPlugins);
216
217 bool RestApiPost(const std::string& uri,
218 const Json::Value& body,
219 bool applyPlugins);
220
221 bool RestApiPut(const std::string& uri,
222 const Json::Value& body,
223 bool applyPlugins);
224
225 bool RestApiPost(const std::string& uri,
226 const std::string& body,
227 bool applyPlugins)
228 {
229 return RestApiPost(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins);
230 }
231
232 bool RestApiPut(const std::string& uri,
233 const std::string& body,
234 bool applyPlugins)
235 {
236 return RestApiPut(uri, body.empty() ? NULL : body.c_str(), body.size(), applyPlugins);
237 }
238
239 void CreateDicom(const Json::Value& tags,
240 OrthancPluginCreateDicomFlags flags);
241
242 void CreateDicom(const Json::Value& tags,
243 const OrthancImage& pixelData,
244 OrthancPluginCreateDicomFlags flags);
245
246 void ReadFile(const std::string& path);
247
248 void GetDicomQuery(const OrthancPluginWorklistQuery* query);
249
250 void DicomToJson(Json::Value& target,
251 OrthancPluginDicomToJsonFormat format,
252 OrthancPluginDicomToJsonFlags flags,
253 uint32_t maxStringLength);
254
255 bool HttpGet(const std::string& url,
256 const std::string& username,
257 const std::string& password);
258
259 bool HttpPost(const std::string& url,
260 const std::string& body,
261 const std::string& username,
262 const std::string& password);
263
264 bool HttpPut(const std::string& url,
265 const std::string& body,
266 const std::string& username,
267 const std::string& password);
268
269 void GetDicomInstance(const std::string& instanceId);
270 };
271
272
273 class OrthancString : public boost::noncopyable
274 {
275 private:
276 char* str_;
277
278 void Clear();
279
280 public:
281 OrthancString() :
282 str_(NULL)
283 {
284 }
285
286 ~OrthancString()
287 {
288 Clear();
289 }
290
291 // This transfers ownership, warning: The string must have been
292 // allocated by the Orthanc core
293 void Assign(char* str);
294
295 const char* GetContent() const
296 {
297 return str_;
298 }
299
300 void ToString(std::string& target) const;
301
302 void ToJson(Json::Value& target) const;
303 };
304
305
306 class OrthancConfiguration : public boost::noncopyable
307 {
308 private:
309 Json::Value configuration_; // Necessarily a Json::objectValue
310 std::string path_;
311
312 std::string GetPath(const std::string& key) const;
313
314 void LoadConfiguration();
315
316 public:
317 OrthancConfiguration();
318
319 explicit OrthancConfiguration(bool load);
320
321 const Json::Value& GetJson() const
322 {
323 return configuration_;
324 }
325
326 bool IsSection(const std::string& key) const;
327
328 void GetSection(OrthancConfiguration& target,
329 const std::string& key) const;
330
331 bool LookupStringValue(std::string& target,
332 const std::string& key) const;
333
334 bool LookupIntegerValue(int& target,
335 const std::string& key) const;
336
337 bool LookupUnsignedIntegerValue(unsigned int& target,
338 const std::string& key) const;
339
340 bool LookupBooleanValue(bool& target,
341 const std::string& key) const;
342
343 bool LookupFloatValue(float& target,
344 const std::string& key) const;
345
346 bool LookupListOfStrings(std::list<std::string>& target,
347 const std::string& key,
348 bool allowSingleString) const;
349
350 bool LookupSetOfStrings(std::set<std::string>& target,
351 const std::string& key,
352 bool allowSingleString) const;
353
354 std::string GetStringValue(const std::string& key,
355 const std::string& defaultValue) const;
356
357 int GetIntegerValue(const std::string& key,
358 int defaultValue) const;
359
360 unsigned int GetUnsignedIntegerValue(const std::string& key,
361 unsigned int defaultValue) const;
362
363 bool GetBooleanValue(const std::string& key,
364 bool defaultValue) const;
365
366 float GetFloatValue(const std::string& key,
367 float defaultValue) const;
368
369 void GetDictionary(std::map<std::string, std::string>& target,
370 const std::string& key) const;
371 };
372
373 class OrthancImage : public boost::noncopyable
374 {
375 private:
376 OrthancPluginImage* image_;
377
378 void Clear();
379
380 void CheckImageAvailable() const;
381
382 public:
383 OrthancImage();
384
385 explicit OrthancImage(OrthancPluginImage* image);
386
387 OrthancImage(OrthancPluginPixelFormat format,
388 uint32_t width,
389 uint32_t height);
390
391 OrthancImage(OrthancPluginPixelFormat format,
392 uint32_t width,
393 uint32_t height,
394 uint32_t pitch,
395 void* buffer);
396
397 ~OrthancImage()
398 {
399 Clear();
400 }
401
402 void UncompressPngImage(const void* data,
403 size_t size);
404
405 void UncompressJpegImage(const void* data,
406 size_t size);
407
408 void DecodeDicomImage(const void* data,
409 size_t size,
410 unsigned int frame);
411
412 OrthancPluginPixelFormat GetPixelFormat() const;
413
414 unsigned int GetWidth() const;
415
416 unsigned int GetHeight() const;
417
418 unsigned int GetPitch() const;
419
420 void* GetBuffer() const;
421
422 const OrthancPluginImage* GetObject() const
423 {
424 return image_;
425 }
426
427 void CompressPngImage(MemoryBuffer& target) const;
428
429 void CompressJpegImage(MemoryBuffer& target,
430 uint8_t quality) const;
431
432 void AnswerPngImage(OrthancPluginRestOutput* output) const;
433
434 void AnswerJpegImage(OrthancPluginRestOutput* output,
435 uint8_t quality) const;
436
437 void* GetWriteableBuffer();
438
439 OrthancPluginImage* Release();
440 };
441
442
443 #if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
444 class FindMatcher : public boost::noncopyable
445 {
446 private:
447 OrthancPluginFindMatcher* matcher_;
448 const OrthancPluginWorklistQuery* worklist_;
449
450 void SetupDicom(const void* query,
451 uint32_t size);
452
453 public:
454 explicit FindMatcher(const OrthancPluginWorklistQuery* worklist);
455
456 FindMatcher(const void* query,
457 uint32_t size)
458 {
459 SetupDicom(query, size);
460 }
461
462 explicit FindMatcher(const MemoryBuffer& dicom)
463 {
464 SetupDicom(dicom.GetData(), dicom.GetSize());
465 }
466
467 ~FindMatcher();
468
469 bool IsMatch(const void* dicom,
470 uint32_t size) const;
471
472 bool IsMatch(const MemoryBuffer& dicom) const
473 {
474 return IsMatch(dicom.GetData(), dicom.GetSize());
475 }
476 };
477 #endif
478
479
480 bool ReadJson(Json::Value& target,
481 const std::string& source);
482
483 bool ReadJson(Json::Value& target,
484 const void* buffer,
485 size_t size);
486
487 bool ReadJsonWithoutComments(Json::Value& target,
488 const std::string& source);
489
490 bool ReadJsonWithoutComments(Json::Value& target,
491 const void* buffer,
492 size_t size);
493
494 void WriteFastJson(std::string& target,
495 const Json::Value& source);
496
497 void WriteStyledJson(std::string& target,
498 const Json::Value& source);
499
500 bool RestApiGet(Json::Value& result,
501 const std::string& uri,
502 bool applyPlugins);
503
504 bool RestApiGetString(std::string& result,
505 const std::string& uri,
506 bool applyPlugins);
507
508 bool RestApiGetString(std::string& result,
509 const std::string& uri,
510 const std::map<std::string, std::string>& httpHeaders,
511 bool applyPlugins);
512
513 bool RestApiPost(std::string& result,
514 const std::string& uri,
515 const void* body,
516 size_t bodySize,
517 bool applyPlugins);
518
519 bool RestApiPost(Json::Value& result,
520 const std::string& uri,
521 const void* body,
522 size_t bodySize,
523 bool applyPlugins);
524
525 bool RestApiPost(Json::Value& result,
526 const std::string& uri,
527 const Json::Value& body,
528 bool applyPlugins);
529
530 inline bool RestApiPost(Json::Value& result,
531 const std::string& uri,
532 const std::string& body,
533 bool applyPlugins)
534 {
535 return RestApiPost(result, uri, body.empty() ? NULL : body.c_str(),
536 body.size(), applyPlugins);
537 }
538
539 inline bool RestApiPost(Json::Value& result,
540 const std::string& uri,
541 const MemoryBuffer& body,
542 bool applyPlugins)
543 {
544 return RestApiPost(result, uri, body.GetData(),
545 body.GetSize(), applyPlugins);
546 }
547
548 bool RestApiPut(Json::Value& result,
549 const std::string& uri,
550 const void* body,
551 size_t bodySize,
552 bool applyPlugins);
553
554 bool RestApiPut(Json::Value& result,
555 const std::string& uri,
556 const Json::Value& body,
557 bool applyPlugins);
558
559 inline bool RestApiPut(Json::Value& result,
560 const std::string& uri,
561 const std::string& body,
562 bool applyPlugins)
563 {
564 return RestApiPut(result, uri, body.empty() ? NULL : body.c_str(),
565 body.size(), applyPlugins);
566 }
567
568 bool RestApiDelete(const std::string& uri,
569 bool applyPlugins);
570
571 bool HttpDelete(const std::string& url,
572 const std::string& username,
573 const std::string& password);
574
575 void AnswerJson(const Json::Value& value,
576 OrthancPluginRestOutput* output);
577
578 void AnswerString(const std::string& answer,
579 const char* mimeType,
580 OrthancPluginRestOutput* output);
581
582 void AnswerHttpError(uint16_t httpError,
583 OrthancPluginRestOutput* output);
584
585 void AnswerMethodNotAllowed(OrthancPluginRestOutput* output, const char* allowedMethods);
586
587 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
588 const char* AutodetectMimeType(const std::string& path);
589 #endif
590
591 void LogError(const std::string& message);
592
593 void LogWarning(const std::string& message);
594
595 void LogInfo(const std::string& message);
596
597 void ReportMinimalOrthancVersion(unsigned int major,
598 unsigned int minor,
599 unsigned int revision);
600
601 bool CheckMinimalOrthancVersion(unsigned int major,
602 unsigned int minor,
603 unsigned int revision);
604
605
606 namespace Internals
607 {
608 template <RestCallback Callback>
609 static OrthancPluginErrorCode Protect(OrthancPluginRestOutput* output,
610 const char* url,
611 const OrthancPluginHttpRequest* request)
612 {
613 try
614 {
615 Callback(output, url, request);
616 return OrthancPluginErrorCode_Success;
617 }
618 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
619 {
620 #if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1
621 if (HasGlobalContext() &&
622 e.HasDetails())
623 {
624 // The "false" instructs Orthanc not to log the detailed
625 // error message. This is to avoid duplicating the details,
626 // because "OrthancException" already does it on construction.
627 OrthancPluginSetHttpErrorDetails
628 (GetGlobalContext(), output, e.GetDetails(), false);
629 }
630 #endif
631
632 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
633 }
634 catch (boost::bad_lexical_cast&)
635 {
636 return OrthancPluginErrorCode_BadFileFormat;
637 }
638 catch (...)
639 {
640 return OrthancPluginErrorCode_Plugin;
641 }
642 }
643 }
644
645
646 template <RestCallback Callback>
647 void RegisterRestCallback(const std::string& uri,
648 bool isThreadSafe)
649 {
650 if (isThreadSafe)
651 {
652 OrthancPluginRegisterRestCallbackNoLock
653 (GetGlobalContext(), uri.c_str(), Internals::Protect<Callback>);
654 }
655 else
656 {
657 OrthancPluginRegisterRestCallback
658 (GetGlobalContext(), uri.c_str(), Internals::Protect<Callback>);
659 }
660 }
661
662
663 #if HAS_ORTHANC_PLUGIN_PEERS == 1
664 class OrthancPeers : public boost::noncopyable
665 {
666 private:
667 typedef std::map<std::string, uint32_t> Index;
668
669 OrthancPluginPeers *peers_;
670 Index index_;
671 uint32_t timeout_;
672
673 size_t GetPeerIndex(const std::string& name) const;
674
675 public:
676 OrthancPeers();
677
678 ~OrthancPeers();
679
680 uint32_t GetTimeout() const
681 {
682 return timeout_;
683 }
684
685 void SetTimeout(uint32_t timeout)
686 {
687 timeout_ = timeout;
688 }
689
690 bool LookupName(size_t& target,
691 const std::string& name) const;
692
693 std::string GetPeerName(size_t index) const;
694
695 std::string GetPeerUrl(size_t index) const;
696
697 std::string GetPeerUrl(const std::string& name) const;
698
699 size_t GetPeersCount() const
700 {
701 return index_.size();
702 }
703
704 bool LookupUserProperty(std::string& value,
705 size_t index,
706 const std::string& key) const;
707
708 bool LookupUserProperty(std::string& value,
709 const std::string& peer,
710 const std::string& key) const;
711
712 bool DoGet(MemoryBuffer& target,
713 size_t index,
714 const std::string& uri) const;
715
716 bool DoGet(MemoryBuffer& target,
717 const std::string& name,
718 const std::string& uri) const;
719
720 bool DoGet(Json::Value& target,
721 size_t index,
722 const std::string& uri) const;
723
724 bool DoGet(Json::Value& target,
725 const std::string& name,
726 const std::string& uri) const;
727
728 bool DoPost(MemoryBuffer& target,
729 size_t index,
730 const std::string& uri,
731 const std::string& body) const;
732
733 bool DoPost(MemoryBuffer& target,
734 const std::string& name,
735 const std::string& uri,
736 const std::string& body) const;
737
738 bool DoPost(Json::Value& target,
739 size_t index,
740 const std::string& uri,
741 const std::string& body) const;
742
743 bool DoPost(Json::Value& target,
744 const std::string& name,
745 const std::string& uri,
746 const std::string& body) const;
747
748 bool DoPut(size_t index,
749 const std::string& uri,
750 const std::string& body) const;
751
752 bool DoPut(const std::string& name,
753 const std::string& uri,
754 const std::string& body) const;
755
756 bool DoDelete(size_t index,
757 const std::string& uri) const;
758
759 bool DoDelete(const std::string& name,
760 const std::string& uri) const;
761 };
762 #endif
763
764
765
766 #if HAS_ORTHANC_PLUGIN_JOB == 1
767 class OrthancJob : public boost::noncopyable
768 {
769 private:
770 std::string jobType_;
771 std::string content_;
772 bool hasSerialized_;
773 std::string serialized_;
774 float progress_;
775
776 static void CallbackFinalize(void* job);
777
778 static float CallbackGetProgress(void* job);
779
780 static const char* CallbackGetContent(void* job);
781
782 static const char* CallbackGetSerialized(void* job);
783
784 static OrthancPluginJobStepStatus CallbackStep(void* job);
785
786 static OrthancPluginErrorCode CallbackStop(void* job,
787 OrthancPluginJobStopReason reason);
788
789 static OrthancPluginErrorCode CallbackReset(void* job);
790
791 protected:
792 void ClearContent();
793
794 void UpdateContent(const Json::Value& content);
795
796 void ClearSerialized();
797
798 void UpdateSerialized(const Json::Value& serialized);
799
800 void UpdateProgress(float progress);
801
802 public:
803 explicit OrthancJob(const std::string& jobType);
804
805 virtual ~OrthancJob()
806 {
807 }
808
809 virtual OrthancPluginJobStepStatus Step() = 0;
810
811 virtual void Stop(OrthancPluginJobStopReason reason) = 0;
812
813 virtual void Reset() = 0;
814
815 static OrthancPluginJob* Create(OrthancJob* job /* takes ownership */);
816
817 static std::string Submit(OrthancJob* job /* takes ownership */,
818 int priority);
819
820 static void SubmitAndWait(Json::Value& result,
821 OrthancJob* job /* takes ownership */,
822 int priority);
823
824 // Submit a job from a POST on the REST API with the same
825 // conventions as in the Orthanc core (according to the
826 // "Synchronous" and "Priority" options)
827 static void SubmitFromRestApiPost(OrthancPluginRestOutput* output,
828 const Json::Value& body,
829 OrthancJob* job);
830 };
831 #endif
832
833
834 #if HAS_ORTHANC_PLUGIN_METRICS == 1
835 inline void SetMetricsValue(char* name,
836 float value)
837 {
838 OrthancPluginSetMetricsValue(GetGlobalContext(), name,
839 value, OrthancPluginMetricsType_Default);
840 }
841
842 class MetricsTimer : public boost::noncopyable
843 {
844 private:
845 std::string name_;
846 boost::posix_time::ptime start_;
847
848 public:
849 explicit MetricsTimer(const char* name);
850
851 ~MetricsTimer();
852 };
853 #endif
854
855
856 #if HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1
857 class HttpClient : public boost::noncopyable
858 {
859 public:
860 typedef std::map<std::string, std::string> HttpHeaders;
861
862 class IRequestBody : public boost::noncopyable
863 {
864 public:
865 virtual ~IRequestBody()
866 {
867 }
868
869 virtual bool ReadNextChunk(std::string& chunk) = 0;
870 };
871
872
873 class IAnswer : public boost::noncopyable
874 {
875 public:
876 virtual ~IAnswer()
877 {
878 }
879
880 virtual void AddHeader(const std::string& key,
881 const std::string& value) = 0;
882
883 virtual void AddChunk(const void* data,
884 size_t size) = 0;
885 };
886
887
888 private:
889 class RequestBodyWrapper;
890
891 uint16_t httpStatus_;
892 OrthancPluginHttpMethod method_;
893 std::string url_;
894 HttpHeaders headers_;
895 std::string username_;
896 std::string password_;
897 uint32_t timeout_;
898 std::string certificateFile_;
899 std::string certificateKeyFile_;
900 std::string certificateKeyPassword_;
901 bool pkcs11_;
902 std::string fullBody_;
903 IRequestBody* chunkedBody_;
904 bool allowChunkedTransfers_;
905
906 #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
907 void ExecuteWithStream(uint16_t& httpStatus, // out
908 IAnswer& answer, // out
909 IRequestBody& body) const;
910 #endif
911
912 void ExecuteWithoutStream(uint16_t& httpStatus, // out
913 HttpHeaders& answerHeaders, // out
914 std::string& answerBody, // out
915 const std::string& body) const;
916
917 public:
918 HttpClient();
919
920 uint16_t GetHttpStatus() const
921 {
922 return httpStatus_;
923 }
924
925 void SetMethod(OrthancPluginHttpMethod method)
926 {
927 method_ = method;
928 }
929
930 const std::string& GetUrl() const
931 {
932 return url_;
933 }
934
935 void SetUrl(const std::string& url)
936 {
937 url_ = url;
938 }
939
940 void SetHeaders(const HttpHeaders& headers)
941 {
942 headers_ = headers;
943 }
944
945 void AddHeader(const std::string& key,
946 const std::string& value)
947 {
948 headers_[key] = value;
949 }
950
951 void AddHeaders(const HttpHeaders& headers);
952
953 void SetCredentials(const std::string& username,
954 const std::string& password);
955
956 void ClearCredentials();
957
958 void SetTimeout(unsigned int timeout) // 0 for default timeout
959 {
960 timeout_ = timeout;
961 }
962
963 void SetCertificate(const std::string& certificateFile,
964 const std::string& keyFile,
965 const std::string& keyPassword);
966
967 void ClearCertificate();
968
969 void SetPkcs11(bool pkcs11)
970 {
971 pkcs11_ = pkcs11;
972 }
973
974 void ClearBody();
975
976 void SwapBody(std::string& body);
977
978 void SetBody(const std::string& body);
979
980 void SetBody(IRequestBody& body);
981
982 // This function can be used to disable chunked transfers if the
983 // remote server is Orthanc with a version <= 1.5.6.
984 void SetChunkedTransfersAllowed(bool allow)
985 {
986 allowChunkedTransfers_ = allow;
987 }
988
989 bool IsChunkedTransfersAllowed() const
990 {
991 return allowChunkedTransfers_;
992 }
993
994 void Execute(IAnswer& answer);
995
996 void Execute(HttpHeaders& answerHeaders /* out */,
997 std::string& answerBody /* out */);
998
999 void Execute(HttpHeaders& answerHeaders /* out */,
1000 Json::Value& answerBody /* out */);
1001
1002 void Execute();
1003 };
1004 #endif
1005
1006
1007
1008 class IChunkedRequestReader : public boost::noncopyable
1009 {
1010 public:
1011 virtual ~IChunkedRequestReader()
1012 {
1013 }
1014
1015 virtual void AddChunk(const void* data,
1016 size_t size) = 0;
1017
1018 virtual void Execute(OrthancPluginRestOutput* output) = 0;
1019 };
1020
1021
1022 typedef IChunkedRequestReader* (*ChunkedRestCallback) (const char* url,
1023 const OrthancPluginHttpRequest* request);
1024
1025
1026 namespace Internals
1027 {
1028 void NullRestCallback(OrthancPluginRestOutput* output,
1029 const char* url,
1030 const OrthancPluginHttpRequest* request);
1031
1032 IChunkedRequestReader *NullChunkedRestCallback(const char* url,
1033 const OrthancPluginHttpRequest* request);
1034
1035
1036 #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
1037 template <ChunkedRestCallback Callback>
1038 static OrthancPluginErrorCode ChunkedProtect(OrthancPluginServerChunkedRequestReader** reader,
1039 const char* url,
1040 const OrthancPluginHttpRequest* request)
1041 {
1042 try
1043 {
1044 if (reader == NULL)
1045 {
1046 return OrthancPluginErrorCode_InternalError;
1047 }
1048 else
1049 {
1050 *reader = reinterpret_cast<OrthancPluginServerChunkedRequestReader*>(Callback(url, request));
1051 if (*reader == NULL)
1052 {
1053 return OrthancPluginErrorCode_Plugin;
1054 }
1055 else
1056 {
1057 return OrthancPluginErrorCode_Success;
1058 }
1059 }
1060 }
1061 catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
1062 {
1063 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode());
1064 }
1065 catch (boost::bad_lexical_cast&)
1066 {
1067 return OrthancPluginErrorCode_BadFileFormat;
1068 }
1069 catch (...)
1070 {
1071 return OrthancPluginErrorCode_Plugin;
1072 }
1073 }
1074
1075 OrthancPluginErrorCode ChunkedRequestReaderAddChunk(
1076 OrthancPluginServerChunkedRequestReader* reader,
1077 const void* data,
1078 uint32_t size);
1079
1080 OrthancPluginErrorCode ChunkedRequestReaderExecute(
1081 OrthancPluginServerChunkedRequestReader* reader,
1082 OrthancPluginRestOutput* output);
1083
1084 void ChunkedRequestReaderFinalize(
1085 OrthancPluginServerChunkedRequestReader* reader);
1086
1087 #else
1088
1089 OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output,
1090 const char* url,
1091 const OrthancPluginHttpRequest* request,
1092 RestCallback GetHandler,
1093 ChunkedRestCallback PostHandler,
1094 RestCallback DeleteHandler,
1095 ChunkedRestCallback PutHandler);
1096
1097 template<
1098 RestCallback GetHandler,
1099 ChunkedRestCallback PostHandler,
1100 RestCallback DeleteHandler,
1101 ChunkedRestCallback PutHandler
1102 >
1103 inline OrthancPluginErrorCode ChunkedRestCompatibility(OrthancPluginRestOutput* output,
1104 const char* url,
1105 const OrthancPluginHttpRequest* request)
1106 {
1107 return ChunkedRestCompatibility(output, url, request, GetHandler,
1108 PostHandler, DeleteHandler, PutHandler);
1109 }
1110 #endif
1111 }
1112
1113
1114
1115 // NB: We use a templated class instead of a templated function, because
1116 // default values are only available in functions since C++11
1117 template<
1118 RestCallback GetHandler = Internals::NullRestCallback,
1119 ChunkedRestCallback PostHandler = Internals::NullChunkedRestCallback,
1120 RestCallback DeleteHandler = Internals::NullRestCallback,
1121 ChunkedRestCallback PutHandler = Internals::NullChunkedRestCallback
1122 >
1123 class ChunkedRestRegistration : public boost::noncopyable
1124 {
1125 public:
1126 static void Apply(const std::string& uri)
1127 {
1128 #if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
1129 OrthancPluginRegisterChunkedRestCallback(
1130 GetGlobalContext(), uri.c_str(),
1131 GetHandler == Internals::NullRestCallback ? NULL : Internals::Protect<GetHandler>,
1132 PostHandler == Internals::NullChunkedRestCallback ? NULL : Internals::ChunkedProtect<PostHandler>,
1133 DeleteHandler == Internals::NullRestCallback ? NULL : Internals::Protect<DeleteHandler>,
1134 PutHandler == Internals::NullChunkedRestCallback ? NULL : Internals::ChunkedProtect<PutHandler>,
1135 Internals::ChunkedRequestReaderAddChunk,
1136 Internals::ChunkedRequestReaderExecute,
1137 Internals::ChunkedRequestReaderFinalize);
1138 #else
1139 OrthancPluginRegisterRestCallbackNoLock(
1140 GetGlobalContext(), uri.c_str(),
1141 Internals::ChunkedRestCompatibility<GetHandler, PostHandler, DeleteHandler, PutHandler>);
1142 #endif
1143 }
1144 };
1145
1146
1147
1148 #if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
1149 class IStorageCommitmentScpHandler : public boost::noncopyable
1150 {
1151 public:
1152 virtual ~IStorageCommitmentScpHandler()
1153 {
1154 }
1155
1156 virtual OrthancPluginStorageCommitmentFailureReason Lookup(const std::string& sopClassUid,
1157 const std::string& sopInstanceUid) = 0;
1158
1159 static OrthancPluginErrorCode Lookup(OrthancPluginStorageCommitmentFailureReason* target,
1160 void* rawHandler,
1161 const char* sopClassUid,
1162 const char* sopInstanceUid);
1163
1164 static void Destructor(void* rawHandler);
1165 };
1166 #endif
1167
1168
1169 class DicomInstance : public boost::noncopyable
1170 {
1171 private:
1172 bool toFree_;
1173
1174 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
1175 const OrthancPluginDicomInstance* instance_;
1176 #else
1177 OrthancPluginDicomInstance* instance_;
1178 #endif
1179
1180 public:
1181 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
1182 explicit DicomInstance(const OrthancPluginDicomInstance* instance);
1183 #else
1184 explicit DicomInstance(OrthancPluginDicomInstance* instance);
1185 #endif
1186
1187 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
1188 DicomInstance(const void* buffer,
1189 size_t size);
1190 #endif
1191
1192 ~DicomInstance();
1193
1194 std::string GetRemoteAet() const;
1195
1196 const void* GetBuffer() const
1197 {
1198 return OrthancPluginGetInstanceData(GetGlobalContext(), instance_);
1199 }
1200
1201 size_t GetSize() const
1202 {
1203 return static_cast<size_t>(OrthancPluginGetInstanceSize(GetGlobalContext(), instance_));
1204 }
1205
1206 void GetJson(Json::Value& target) const;
1207
1208 void GetSimplifiedJson(Json::Value& target) const;
1209
1210 OrthancPluginInstanceOrigin GetOrigin() const
1211 {
1212 return OrthancPluginGetInstanceOrigin(GetGlobalContext(), instance_);
1213 }
1214
1215 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
1216 std::string GetTransferSyntaxUid() const;
1217 #endif
1218
1219 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 1)
1220 bool HasPixelData() const;
1221 #endif
1222
1223 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
1224 unsigned int GetFramesCount() const
1225 {
1226 return OrthancPluginGetInstanceFramesCount(GetGlobalContext(), instance_);
1227 }
1228 #endif
1229
1230 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
1231 void GetRawFrame(std::string& target,
1232 unsigned int frameIndex) const;
1233 #endif
1234
1235 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
1236 OrthancImage* GetDecodedFrame(unsigned int frameIndex) const;
1237 #endif
1238
1239 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
1240 void Serialize(std::string& target) const;
1241 #endif
1242
1243 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 7, 0)
1244 static DicomInstance* Transcode(const void* buffer,
1245 size_t size,
1246 const std::string& transferSyntax);
1247 #endif
1248 };
1249 }