comparison Applications/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h @ 1538:d1806b4e4839

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