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  * In addition, as a special exception, the copyright holders of this
13  * program give permission to link the code of its release with the
14  * OpenSSL project's "OpenSSL" library (or with modified versions of it
15  * that use the same license as the "OpenSSL" library), and distribute
16  * the linked executables. You must obey the GNU General Public License
17  * in all respects for all of the code used other than "OpenSSL". If you
18  * modify file(s) with this exception, you may extend this exception to
19  * your version of the file(s), but you are not obligated to do so. If
20  * you do not wish to do so, delete this exception statement from your
21  * version. If you delete this exception statement from all source files
22  * in the program, then also delete it here.
23  *
24  * This program is distributed in the hope that it will be useful, but
25  * WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27  * General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program. If not, see <http://www.gnu.org/licenses/>.
31  **/
32 
33 
34 #include "../../Sources/PrecompiledHeadersServer.h"
35 #include "OrthancPlugins.h"
36 
37 #if ORTHANC_ENABLE_PLUGINS != 1
38 #error The plugin support is disabled
39 #endif
40 
41 #if !defined(DCMTK_VERSION_NUMBER)
42 #  error The macro DCMTK_VERSION_NUMBER must be defined
43 #endif
44 
45 
46 #include "../../../OrthancFramework/Sources/Compression/GzipCompressor.h"
47 #include "../../../OrthancFramework/Sources/Compression/ZlibCompressor.h"
48 #include "../../../OrthancFramework/Sources/DicomFormat/DicomArray.h"
49 #include "../../../OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h"
50 #include "../../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h"
51 #include "../../../OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.h"
52 #include "../../../OrthancFramework/Sources/DicomParsing/ToDcmtkBridge.h"
53 #include "../../../OrthancFramework/Sources/HttpServer/HttpToolbox.h"
54 #include "../../../OrthancFramework/Sources/Images/Image.h"
55 #include "../../../OrthancFramework/Sources/Images/ImageProcessing.h"
56 #include "../../../OrthancFramework/Sources/Images/JpegReader.h"
57 #include "../../../OrthancFramework/Sources/Images/JpegWriter.h"
58 #include "../../../OrthancFramework/Sources/Images/PngReader.h"
59 #include "../../../OrthancFramework/Sources/Images/PngWriter.h"
60 #include "../../../OrthancFramework/Sources/Logging.h"
61 #include "../../../OrthancFramework/Sources/Lua/LuaFunctionCall.h"
62 #include "../../../OrthancFramework/Sources/MallocMemoryBuffer.h"
63 #include "../../../OrthancFramework/Sources/MetricsRegistry.h"
64 #include "../../../OrthancFramework/Sources/OrthancException.h"
65 #include "../../../OrthancFramework/Sources/SerializationToolbox.h"
66 #include "../../../OrthancFramework/Sources/StringMemoryBuffer.h"
67 #include "../../../OrthancFramework/Sources/Toolbox.h"
68 #include "../../Sources/Database/VoidDatabaseListener.h"
69 #include "../../Sources/OrthancConfiguration.h"
70 #include "../../Sources/OrthancFindRequestHandler.h"
71 #include "../../Sources/Search/HierarchicalMatcher.h"
72 #include "../../Sources/ServerContext.h"
73 #include "../../Sources/ServerToolbox.h"
74 #include "OrthancPluginDatabase.h"
75 #include "OrthancPluginDatabaseV3.h"
76 #include "PluginsEnumerations.h"
77 #include "PluginsJob.h"
78 
79 #include <boost/regex.hpp>
80 #include <dcmtk/dcmdata/dcdict.h>
81 #include <dcmtk/dcmdata/dcdicent.h>
82 
83 #define ERROR_MESSAGE_64BIT "A 64bit version of the Orthanc API is necessary"
84 
85 
86 namespace Orthanc
87 {
CopyToMemoryBuffer(OrthancPluginMemoryBuffer & target,const void * data,size_t size)88   static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
89                                  const void* data,
90                                  size_t size)
91   {
92     if (static_cast<uint32_t>(size) != size)
93     {
94       throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
95     }
96 
97     target.size = size;
98 
99     if (size == 0)
100     {
101       target.data = NULL;
102     }
103     else
104     {
105       target.data = malloc(size);
106       if (target.data != NULL)
107       {
108         memcpy(target.data, data, size);
109       }
110       else
111       {
112         throw OrthancException(ErrorCode_NotEnoughMemory);
113       }
114     }
115   }
116 
117 
CopyToMemoryBuffer(OrthancPluginMemoryBuffer & target,const std::string & str)118   static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
119                                  const std::string& str)
120   {
121     if (str.size() == 0)
122     {
123       target.size = 0;
124       target.data = NULL;
125     }
126     else
127     {
128       CopyToMemoryBuffer(target, str.c_str(), str.size());
129     }
130   }
131 
132 
CopyString(const std::string & str)133   static char* CopyString(const std::string& str)
134   {
135     if (static_cast<uint32_t>(str.size()) != str.size())
136     {
137       throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
138     }
139 
140     char *result = reinterpret_cast<char*>(malloc(str.size() + 1));
141     if (result == NULL)
142     {
143       throw OrthancException(ErrorCode_NotEnoughMemory);
144     }
145 
146     if (str.size() == 0)
147     {
148       result[0] = '\0';
149     }
150     else
151     {
152       memcpy(result, &str[0], str.size() + 1);
153     }
154 
155     return result;
156   }
157 
158 
CopyDictionary(OrthancPluginMemoryBuffer & target,const std::map<std::string,std::string> & dictionary)159   static void CopyDictionary(OrthancPluginMemoryBuffer& target,
160                              const std::map<std::string, std::string>& dictionary)
161   {
162     Json::Value json = Json::objectValue;
163 
164     for (HttpClient::HttpHeaders::const_iterator
165            it = dictionary.begin(); it != dictionary.end(); ++it)
166     {
167       json[it->first] = it->second;
168     }
169 
170     std::string s = json.toStyledString();
171     CopyToMemoryBuffer(target, s);
172   }
173 
174 
175   namespace
176   {
177     class MemoryBufferRaii : public boost::noncopyable
178     {
179     private:
180       OrthancPluginMemoryBuffer  buffer_;
181 
182     public:
MemoryBufferRaii()183       MemoryBufferRaii()
184       {
185         buffer_.size = 0;
186         buffer_.data = NULL;
187       }
188 
~MemoryBufferRaii()189       ~MemoryBufferRaii()
190       {
191         if (buffer_.size != 0)
192         {
193           free(buffer_.data);
194         }
195       }
196 
GetObject()197       OrthancPluginMemoryBuffer* GetObject()
198       {
199         return &buffer_;
200       }
201 
ToString(std::string & target) const202       void ToString(std::string& target) const
203       {
204         if ((buffer_.data == NULL && buffer_.size != 0) ||
205             (buffer_.data != NULL && buffer_.size == 0))
206         {
207           throw OrthancException(ErrorCode_Plugin);
208         }
209         else
210         {
211           target.resize(buffer_.size);
212 
213           if (buffer_.size != 0)
214           {
215             memcpy(&target[0], buffer_.data, buffer_.size);
216           }
217         }
218       }
219     };
220 
221 
222     class StorageAreaBase : public IStorageArea
223     {
224     private:
225       OrthancPluginStorageCreate create_;
226       OrthancPluginStorageRemove remove_;
227       PluginsErrorDictionary&    errorDictionary_;
228 
229     protected:
GetErrorDictionary() const230       PluginsErrorDictionary& GetErrorDictionary() const
231       {
232         return errorDictionary_;
233       }
234 
RangeFromWhole(const std::string & uuid,FileContentType type,uint64_t start,uint64_t end)235       IMemoryBuffer* RangeFromWhole(const std::string& uuid,
236                                     FileContentType type,
237                                     uint64_t start /* inclusive */,
238                                     uint64_t end /* exclusive */)
239       {
240         if (start > end)
241         {
242           throw OrthancException(ErrorCode_BadRange);
243         }
244         else if (start == end)
245         {
246           return new StringMemoryBuffer;  // Empty
247         }
248         else
249         {
250           std::unique_ptr<IMemoryBuffer> whole(Read(uuid, type));
251 
252           if (start == 0 &&
253               end == whole->GetSize())
254           {
255             return whole.release();
256           }
257           else if (end > whole->GetSize())
258           {
259             throw OrthancException(ErrorCode_BadRange);
260           }
261           else
262           {
263             std::string range;
264             range.resize(end - start);
265             assert(!range.empty());
266 
267             memcpy(&range[0], reinterpret_cast<const char*>(whole->GetData()) + start, range.size());
268 
269             whole.reset(NULL);
270             return StringMemoryBuffer::CreateFromSwap(range);
271           }
272         }
273       }
274 
275     public:
StorageAreaBase(OrthancPluginStorageCreate create,OrthancPluginStorageRemove remove,PluginsErrorDictionary & errorDictionary)276       StorageAreaBase(OrthancPluginStorageCreate create,
277                       OrthancPluginStorageRemove remove,
278                       PluginsErrorDictionary&  errorDictionary) :
279         create_(create),
280         remove_(remove),
281         errorDictionary_(errorDictionary)
282       {
283         if (create_ == NULL ||
284             remove_ == NULL)
285         {
286           throw OrthancException(ErrorCode_Plugin, "Storage area plugin doesn't implement all the required primitives");
287         }
288       }
289 
Create(const std::string & uuid,const void * content,size_t size,FileContentType type)290       virtual void Create(const std::string& uuid,
291                           const void* content,
292                           size_t size,
293                           FileContentType type) ORTHANC_OVERRIDE
294       {
295         OrthancPluginErrorCode error = create_
296           (uuid.c_str(), content, size, Plugins::Convert(type));
297 
298         if (error != OrthancPluginErrorCode_Success)
299         {
300           errorDictionary_.LogError(error, true);
301           throw OrthancException(static_cast<ErrorCode>(error));
302         }
303       }
304 
Remove(const std::string & uuid,FileContentType type)305       virtual void Remove(const std::string& uuid,
306                           FileContentType type) ORTHANC_OVERRIDE
307       {
308         OrthancPluginErrorCode error = remove_
309           (uuid.c_str(), Plugins::Convert(type));
310 
311         if (error != OrthancPluginErrorCode_Success)
312         {
313           errorDictionary_.LogError(error, true);
314           throw OrthancException(static_cast<ErrorCode>(error));
315         }
316       }
317     };
318 
319 
320     class PluginStorageArea : public StorageAreaBase
321     {
322     private:
323       OrthancPluginStorageRead   read_;
324       OrthancPluginFree          free_;
325 
Free(void * buffer) const326       void Free(void* buffer) const
327       {
328         if (buffer != NULL)
329         {
330           free_(buffer);
331         }
332       }
333 
334     public:
PluginStorageArea(const _OrthancPluginRegisterStorageArea & callbacks,PluginsErrorDictionary & errorDictionary)335       PluginStorageArea(const _OrthancPluginRegisterStorageArea& callbacks,
336                         PluginsErrorDictionary&  errorDictionary) :
337         StorageAreaBase(callbacks.create, callbacks.remove, errorDictionary),
338         read_(callbacks.read),
339         free_(callbacks.free)
340       {
341         if (read_ == NULL)
342         {
343           throw OrthancException(ErrorCode_Plugin, "Storage area plugin doesn't implement the \"Read\" primitive");
344         }
345       }
346 
Read(const std::string & uuid,FileContentType type)347       virtual IMemoryBuffer* Read(const std::string& uuid,
348                                   FileContentType type) ORTHANC_OVERRIDE
349       {
350         std::unique_ptr<MallocMemoryBuffer> result(new MallocMemoryBuffer);
351 
352         void* buffer = NULL;
353         int64_t size = 0;
354 
355         OrthancPluginErrorCode error = read_
356           (&buffer, &size, uuid.c_str(), Plugins::Convert(type));
357 
358         if (error == OrthancPluginErrorCode_Success)
359         {
360           result->Assign(buffer, size, free_);
361           return result.release();
362         }
363         else
364         {
365           GetErrorDictionary().LogError(error, true);
366           throw OrthancException(static_cast<ErrorCode>(error));
367         }
368       }
369 
ReadRange(const std::string & uuid,FileContentType type,uint64_t start,uint64_t end)370       virtual IMemoryBuffer* ReadRange(const std::string& uuid,
371                                        FileContentType type,
372                                        uint64_t start /* inclusive */,
373                                        uint64_t end /* exclusive */) ORTHANC_OVERRIDE
374       {
375         return RangeFromWhole(uuid, type, start, end);
376       }
377 
HasReadRange() const378       virtual bool HasReadRange() const ORTHANC_OVERRIDE
379       {
380         return false;
381       }
382     };
383 
384 
385     // New in Orthanc 1.9.0
386     class PluginStorageArea2 : public StorageAreaBase
387     {
388     private:
389       OrthancPluginStorageReadWhole  readWhole_;
390       OrthancPluginStorageReadRange  readRange_;
391 
392     public:
PluginStorageArea2(const _OrthancPluginRegisterStorageArea2 & callbacks,PluginsErrorDictionary & errorDictionary)393       PluginStorageArea2(const _OrthancPluginRegisterStorageArea2& callbacks,
394                          PluginsErrorDictionary&  errorDictionary) :
395         StorageAreaBase(callbacks.create, callbacks.remove, errorDictionary),
396         readWhole_(callbacks.readWhole),
397         readRange_(callbacks.readRange)
398       {
399         if (readWhole_ == NULL)
400         {
401           throw OrthancException(ErrorCode_Plugin, "Storage area plugin doesn't implement the \"ReadWhole\" primitive");
402         }
403       }
404 
Read(const std::string & uuid,FileContentType type)405       virtual IMemoryBuffer* Read(const std::string& uuid,
406                                   FileContentType type) ORTHANC_OVERRIDE
407       {
408         std::unique_ptr<MallocMemoryBuffer> result(new MallocMemoryBuffer);
409 
410         OrthancPluginMemoryBuffer64 buffer;
411 
412         OrthancPluginErrorCode error = readWhole_(&buffer, uuid.c_str(), Plugins::Convert(type));
413 
414         if (error == OrthancPluginErrorCode_Success)
415         {
416           result->Assign(buffer.data, buffer.size, ::free);
417           return result.release();
418         }
419         else
420         {
421           GetErrorDictionary().LogError(error, true);
422           throw OrthancException(static_cast<ErrorCode>(error));
423         }
424       }
425 
ReadRange(const std::string & uuid,FileContentType type,uint64_t start,uint64_t end)426       virtual IMemoryBuffer* ReadRange(const std::string& uuid,
427                                        FileContentType type,
428                                        uint64_t start /* inclusive */,
429                                        uint64_t end /* exclusive */) ORTHANC_OVERRIDE
430       {
431         if (readRange_ == NULL)
432         {
433           return RangeFromWhole(uuid, type, start, end);
434         }
435         else
436         {
437           if (start > end)
438           {
439             throw OrthancException(ErrorCode_BadRange);
440           }
441           else if (start == end)
442           {
443             return new StringMemoryBuffer;
444           }
445           else
446           {
447             std::string range;
448             range.resize(end - start);
449             assert(!range.empty());
450 
451             OrthancPluginMemoryBuffer64 buffer;
452             buffer.data = &range[0];
453             buffer.size = static_cast<uint64_t>(range.size());
454 
455             OrthancPluginErrorCode error =
456               readRange_(&buffer, uuid.c_str(), Plugins::Convert(type), start);
457 
458             if (error == OrthancPluginErrorCode_Success)
459             {
460               return StringMemoryBuffer::CreateFromSwap(range);
461             }
462             else
463             {
464               GetErrorDictionary().LogError(error, true);
465               throw OrthancException(static_cast<ErrorCode>(error));
466             }
467           }
468         }
469       }
470 
HasReadRange() const471       virtual bool HasReadRange() const ORTHANC_OVERRIDE
472       {
473         return (readRange_ != NULL);
474       }
475     };
476 
477 
478     class StorageAreaFactory : public boost::noncopyable
479     {
480     private:
481       enum Version
482       {
483         Version1,
484         Version2
485       };
486 
487       SharedLibrary&                      sharedLibrary_;
488       Version                             version_;
489       _OrthancPluginRegisterStorageArea   callbacks_;
490       _OrthancPluginRegisterStorageArea2  callbacks2_;
491       PluginsErrorDictionary&             errorDictionary_;
492 
WarnNoReadRange()493       static void WarnNoReadRange()
494       {
495         LOG(WARNING) << "Performance warning: The storage area plugin doesn't implement reading of file ranges";
496       }
497 
498     public:
StorageAreaFactory(SharedLibrary & sharedLibrary,const _OrthancPluginRegisterStorageArea & callbacks,PluginsErrorDictionary & errorDictionary)499       StorageAreaFactory(SharedLibrary& sharedLibrary,
500                          const _OrthancPluginRegisterStorageArea& callbacks,
501                          PluginsErrorDictionary&  errorDictionary) :
502         sharedLibrary_(sharedLibrary),
503         version_(Version1),
504         callbacks_(callbacks),
505         errorDictionary_(errorDictionary)
506       {
507         WarnNoReadRange();
508       }
509 
StorageAreaFactory(SharedLibrary & sharedLibrary,const _OrthancPluginRegisterStorageArea2 & callbacks,PluginsErrorDictionary & errorDictionary)510       StorageAreaFactory(SharedLibrary& sharedLibrary,
511                          const _OrthancPluginRegisterStorageArea2& callbacks,
512                          PluginsErrorDictionary&  errorDictionary) :
513         sharedLibrary_(sharedLibrary),
514         version_(Version2),
515         callbacks2_(callbacks),
516         errorDictionary_(errorDictionary)
517       {
518         if (callbacks.readRange == NULL)
519         {
520           WarnNoReadRange();
521         }
522       }
523 
GetSharedLibrary()524       SharedLibrary&  GetSharedLibrary()
525       {
526         return sharedLibrary_;
527       }
528 
Create() const529       IStorageArea* Create() const
530       {
531         switch (version_)
532         {
533           case Version1:
534             return new PluginStorageArea(callbacks_, errorDictionary_);
535 
536           case Version2:
537             return new PluginStorageArea2(callbacks2_, errorDictionary_);
538 
539           default:
540             throw OrthancException(ErrorCode_InternalError);
541         }
542       }
543     };
544 
545 
546     class OrthancPeers : public boost::noncopyable
547     {
548     private:
549       std::vector<std::string>           names_;
550       std::vector<WebServiceParameters>  parameters_;
551 
CheckIndex(size_t i) const552       void CheckIndex(size_t i) const
553       {
554         assert(names_.size() == parameters_.size());
555         if (i >= names_.size())
556         {
557           throw OrthancException(ErrorCode_ParameterOutOfRange);
558         }
559       }
560 
561     public:
OrthancPeers()562       OrthancPeers()
563       {
564         OrthancConfiguration::ReaderLock lock;
565 
566         std::set<std::string> peers;
567         lock.GetConfiguration().GetListOfOrthancPeers(peers);
568 
569         names_.reserve(peers.size());
570         parameters_.reserve(peers.size());
571 
572         for (std::set<std::string>::const_iterator
573                it = peers.begin(); it != peers.end(); ++it)
574         {
575           WebServiceParameters peer;
576           if (lock.GetConfiguration().LookupOrthancPeer(peer, *it))
577           {
578             names_.push_back(*it);
579             parameters_.push_back(peer);
580           }
581         }
582       }
583 
GetPeersCount() const584       size_t GetPeersCount() const
585       {
586         return names_.size();
587       }
588 
GetPeerName(size_t i) const589       const std::string& GetPeerName(size_t i) const
590       {
591         CheckIndex(i);
592         return names_[i];
593       }
594 
GetPeerParameters(size_t i) const595       const WebServiceParameters& GetPeerParameters(size_t i) const
596       {
597         CheckIndex(i);
598         return parameters_[i];
599       }
600     };
601 
602 
603     class DicomWebBinaryFormatter : public DicomWebJsonVisitor::IBinaryFormatter
604     {
605     private:
606       OrthancPluginDicomWebBinaryCallback   oldCallback_;
607       OrthancPluginDicomWebBinaryCallback2  newCallback_;  // New in Orthanc 1.7.0
608       void*                                 newPayload_;   // New in Orthanc 1.7.0
609       DicomWebJsonVisitor::BinaryMode       currentMode_;
610       std::string                           currentBulkDataUri_;
611 
Setter(OrthancPluginDicomWebNode * node,OrthancPluginDicomWebBinaryMode mode,const char * bulkDataUri)612       static void Setter(OrthancPluginDicomWebNode*       node,
613                          OrthancPluginDicomWebBinaryMode  mode,
614                          const char*                      bulkDataUri)
615       {
616         DicomWebBinaryFormatter& that = *reinterpret_cast<DicomWebBinaryFormatter*>(node);
617 
618         switch (mode)
619         {
620           case OrthancPluginDicomWebBinaryMode_Ignore:
621             that.currentMode_ = DicomWebJsonVisitor::BinaryMode_Ignore;
622             break;
623 
624           case OrthancPluginDicomWebBinaryMode_InlineBinary:
625             that.currentMode_ = DicomWebJsonVisitor::BinaryMode_InlineBinary;
626             break;
627 
628           case OrthancPluginDicomWebBinaryMode_BulkDataUri:
629             if (bulkDataUri == NULL)
630             {
631               throw OrthancException(ErrorCode_NullPointer);
632             }
633 
634             that.currentBulkDataUri_ = bulkDataUri;
635             that.currentMode_ = DicomWebJsonVisitor::BinaryMode_BulkDataUri;
636             break;
637 
638           default:
639             throw OrthancException(ErrorCode_ParameterOutOfRange);
640         }
641       }
642 
643     public:
DicomWebBinaryFormatter(OrthancPluginDicomWebBinaryCallback callback)644       explicit DicomWebBinaryFormatter(OrthancPluginDicomWebBinaryCallback callback) :
645         oldCallback_(callback),
646         newCallback_(NULL),
647         newPayload_(NULL),
648         currentMode_(DicomWebJsonVisitor::BinaryMode_Ignore)
649       {
650       }
651 
DicomWebBinaryFormatter(OrthancPluginDicomWebBinaryCallback2 callback,void * payload)652       DicomWebBinaryFormatter(OrthancPluginDicomWebBinaryCallback2 callback,
653                               void* payload) :
654         oldCallback_(NULL),
655         newCallback_(callback),
656         newPayload_(payload),
657         currentMode_(DicomWebJsonVisitor::BinaryMode_Ignore)
658       {
659       }
660 
Format(std::string & bulkDataUri,const std::vector<DicomTag> & parentTags,const std::vector<size_t> & parentIndexes,const DicomTag & tag,ValueRepresentation vr)661       virtual DicomWebJsonVisitor::BinaryMode Format(std::string& bulkDataUri,
662                                                      const std::vector<DicomTag>& parentTags,
663                                                      const std::vector<size_t>& parentIndexes,
664                                                      const DicomTag& tag,
665                                                      ValueRepresentation vr) ORTHANC_OVERRIDE
666       {
667         if (oldCallback_ == NULL &&
668             newCallback_ == NULL)
669         {
670           return DicomWebJsonVisitor::BinaryMode_InlineBinary;
671         }
672         else
673         {
674           assert(parentTags.size() == parentIndexes.size());
675           std::vector<uint16_t> groups(parentTags.size());
676           std::vector<uint16_t> elements(parentTags.size());
677           std::vector<uint32_t> indexes(parentTags.size());
678 
679           for (size_t i = 0; i < parentTags.size(); i++)
680           {
681             groups[i] = parentTags[i].GetGroup();
682             elements[i] = parentTags[i].GetElement();
683             indexes[i] = static_cast<uint32_t>(parentIndexes[i]);
684           }
685           bool empty = parentTags.empty();
686 
687           currentMode_ = DicomWebJsonVisitor::BinaryMode_Ignore;
688 
689           if (oldCallback_ != NULL)
690           {
691             oldCallback_(reinterpret_cast<OrthancPluginDicomWebNode*>(this),
692                          DicomWebBinaryFormatter::Setter,
693                          static_cast<uint32_t>(parentTags.size()),
694                          (empty ? NULL : &groups[0]),
695                          (empty ? NULL : &elements[0]),
696                          (empty ? NULL : &indexes[0]),
697                          tag.GetGroup(),
698                          tag.GetElement(),
699                          Plugins::Convert(vr));
700           }
701           else
702           {
703             assert(newCallback_ != NULL);
704             newCallback_(reinterpret_cast<OrthancPluginDicomWebNode*>(this),
705                          DicomWebBinaryFormatter::Setter,
706                          static_cast<uint32_t>(parentTags.size()),
707                          (empty ? NULL : &groups[0]),
708                          (empty ? NULL : &elements[0]),
709                          (empty ? NULL : &indexes[0]),
710                          tag.GetGroup(),
711                          tag.GetElement(),
712                          Plugins::Convert(vr),
713                          newPayload_);
714           }
715 
716           bulkDataUri = currentBulkDataUri_;
717           return currentMode_;
718         }
719       }
720 
Apply(char ** target,bool isJson,const ParsedDicomFile & dicom)721       void Apply(char** target,
722                  bool isJson,
723                  const ParsedDicomFile& dicom)
724       {
725         DicomWebJsonVisitor visitor;
726         visitor.SetFormatter(*this);
727 
728         dicom.Apply(visitor);
729 
730         std::string s;
731 
732         if (isJson)
733         {
734           s = visitor.GetResult().toStyledString();
735         }
736         else
737         {
738           visitor.FormatXml(s);
739         }
740 
741         *target = CopyString(s);
742       }
743 
744 
Apply(char ** target,bool isJson,const void * dicom,size_t dicomSize)745       void Apply(char** target,
746                  bool isJson,
747                  const void* dicom,
748                  size_t dicomSize)
749       {
750         ParsedDicomFile parsed(dicom, dicomSize);
751         Apply(target, isJson, parsed);
752       }
753     };
754   }
755 
756 
757   class OrthancPlugins::PImpl
758   {
759   private:
760     boost::mutex   contextMutex_;
761     ServerContext* context_;
762 
763   public:
764     class PluginHttpOutput : public boost::noncopyable
765     {
766     private:
767       enum MultipartState
768       {
769         MultipartState_None,
770         MultipartState_FirstPart,
771         MultipartState_SecondPart,
772         MultipartState_NextParts
773       };
774 
775       HttpOutput&                 output_;
776       std::unique_ptr<std::string>  errorDetails_;
777       bool                        logDetails_;
778       MultipartState              multipartState_;
779       std::string                 multipartSubType_;
780       std::string                 multipartContentType_;
781       std::string                 multipartFirstPart_;
782       std::map<std::string, std::string>  multipartFirstHeaders_;
783 
784     public:
PluginHttpOutput(HttpOutput & output)785       explicit PluginHttpOutput(HttpOutput& output) :
786         output_(output),
787         logDetails_(false),
788         multipartState_(MultipartState_None)
789       {
790       }
791 
GetOutput()792       HttpOutput& GetOutput()
793       {
794         if (multipartState_ == MultipartState_None)
795         {
796           return output_;
797         }
798         else
799         {
800           // Must use "SendMultipartItem()" on multipart streams
801           throw OrthancException(ErrorCode_BadSequenceOfCalls);
802         }
803       }
804 
SetErrorDetails(const std::string & details,bool logDetails)805       void SetErrorDetails(const std::string& details,
806                            bool logDetails)
807       {
808         errorDetails_.reset(new std::string(details));
809         logDetails_ = logDetails;
810       }
811 
HasErrorDetails() const812       bool HasErrorDetails() const
813       {
814         return errorDetails_.get() != NULL;
815       }
816 
IsLogDetails() const817       bool IsLogDetails() const
818       {
819         return logDetails_;
820       }
821 
GetErrorDetails() const822       const std::string& GetErrorDetails() const
823       {
824         if (errorDetails_.get() == NULL)
825         {
826           throw OrthancException(ErrorCode_BadSequenceOfCalls);
827         }
828         else
829         {
830           return *errorDetails_;
831         }
832       }
833 
StartMultipart(const char * subType,const char * contentType)834       void StartMultipart(const char* subType,
835                           const char* contentType)
836       {
837         if (multipartState_ != MultipartState_None)
838         {
839           throw OrthancException(ErrorCode_BadSequenceOfCalls);
840         }
841         else
842         {
843           multipartState_ = MultipartState_FirstPart;
844           multipartSubType_ = subType;
845           multipartContentType_ = contentType;
846         }
847       }
848 
SendMultipartItem(const void * data,size_t size,const std::map<std::string,std::string> & headers)849       void SendMultipartItem(const void* data,
850                              size_t size,
851                              const std::map<std::string, std::string>& headers)
852       {
853         if (size != 0 && data == NULL)
854         {
855           throw OrthancException(ErrorCode_NullPointer);
856         }
857 
858         switch (multipartState_)
859         {
860           case MultipartState_None:
861             // Must call "StartMultipart()" before
862             throw OrthancException(ErrorCode_BadSequenceOfCalls);
863 
864           case MultipartState_FirstPart:
865             multipartFirstPart_.assign(reinterpret_cast<const char*>(data), size);
866             multipartFirstHeaders_ = headers;
867             multipartState_ = MultipartState_SecondPart;
868             break;
869 
870           case MultipartState_SecondPart:
871             // Start an actual stream for chunked transfer as soon as
872             // there are more than 2 elements in the multipart stream
873             output_.StartMultipart(multipartSubType_, multipartContentType_);
874             output_.SendMultipartItem(multipartFirstPart_.c_str(), multipartFirstPart_.size(),
875                                       multipartFirstHeaders_);
876             multipartFirstPart_.clear();  // Release memory
877 
878             output_.SendMultipartItem(data, size, headers);
879             multipartState_ = MultipartState_NextParts;
880             break;
881 
882           case MultipartState_NextParts:
883             output_.SendMultipartItem(data, size, headers);
884             break;
885 
886           default:
887             throw OrthancException(ErrorCode_ParameterOutOfRange);
888         }
889       }
890 
Close(OrthancPluginErrorCode error,PluginsErrorDictionary & dictionary)891       void Close(OrthancPluginErrorCode error,
892                  PluginsErrorDictionary& dictionary)
893       {
894         if (error == OrthancPluginErrorCode_Success)
895         {
896           switch (multipartState_)
897           {
898             case MultipartState_None:
899               assert(!output_.IsWritingMultipart());
900               break;
901 
902             case MultipartState_FirstPart:   // Multipart started, but no part was sent
903             case MultipartState_SecondPart:  // Multipart started, first part is pending
904             {
905               assert(!output_.IsWritingMultipart());
906               std::vector<const void*> parts;
907               std::vector<size_t> sizes;
908               std::vector<const std::map<std::string, std::string>*> headers;
909 
910               if (multipartState_ == MultipartState_SecondPart)
911               {
912                 parts.push_back(multipartFirstPart_.c_str());
913                 sizes.push_back(multipartFirstPart_.size());
914                 headers.push_back(&multipartFirstHeaders_);
915               }
916 
917               output_.AnswerMultipartWithoutChunkedTransfer(multipartSubType_, multipartContentType_,
918                                                             parts, sizes, headers);
919               break;
920             }
921 
922             case MultipartState_NextParts:
923               assert(output_.IsWritingMultipart());
924               output_.CloseMultipart();
925 
926             default:
927               throw OrthancException(ErrorCode_InternalError);
928           }
929         }
930         else
931         {
932           dictionary.LogError(error, false);
933 
934           if (HasErrorDetails())
935           {
936             throw OrthancException(static_cast<ErrorCode>(error),
937                                    GetErrorDetails(),
938                                    IsLogDetails());
939           }
940           else
941           {
942             throw OrthancException(static_cast<ErrorCode>(error));
943           }
944         }
945       }
946     };
947 
948 
949     class RestCallback : public boost::noncopyable
950     {
951     private:
952       boost::regex              regex_;
953       OrthancPluginRestCallback callback_;
954       bool                      mutualExclusion_;
955 
InvokeInternal(PluginHttpOutput & output,const std::string & flatUri,const OrthancPluginHttpRequest & request)956       OrthancPluginErrorCode InvokeInternal(PluginHttpOutput& output,
957                                             const std::string& flatUri,
958                                             const OrthancPluginHttpRequest& request)
959       {
960         return callback_(reinterpret_cast<OrthancPluginRestOutput*>(&output),
961                          flatUri.c_str(),
962                          &request);
963       }
964 
965     public:
RestCallback(const char * regex,OrthancPluginRestCallback callback,bool mutualExclusion)966       RestCallback(const char* regex,
967                    OrthancPluginRestCallback callback,
968                    bool mutualExclusion) :
969         regex_(regex),
970         callback_(callback),
971         mutualExclusion_(mutualExclusion)
972       {
973       }
974 
GetRegularExpression() const975       const boost::regex& GetRegularExpression() const
976       {
977         return regex_;
978       }
979 
Invoke(boost::recursive_mutex & invokationMutex,PluginHttpOutput & output,const std::string & flatUri,const OrthancPluginHttpRequest & request)980       OrthancPluginErrorCode Invoke(boost::recursive_mutex& invokationMutex,
981                                     PluginHttpOutput& output,
982                                     const std::string& flatUri,
983                                     const OrthancPluginHttpRequest& request)
984       {
985         if (mutualExclusion_)
986         {
987           boost::recursive_mutex::scoped_lock lock(invokationMutex);
988           return InvokeInternal(output, flatUri, request);
989         }
990         else
991         {
992           return InvokeInternal(output, flatUri, request);
993         }
994       }
995     };
996 
997 
998     class ChunkedRestCallback : public boost::noncopyable
999     {
1000     private:
1001       _OrthancPluginChunkedRestCallback parameters_;
1002       boost::regex                      regex_;
1003 
1004     public:
ChunkedRestCallback(const _OrthancPluginChunkedRestCallback & parameters)1005       explicit ChunkedRestCallback(const _OrthancPluginChunkedRestCallback& parameters) :
1006         parameters_(parameters),
1007         regex_(parameters.pathRegularExpression)
1008       {
1009       }
1010 
GetRegularExpression() const1011       const boost::regex& GetRegularExpression() const
1012       {
1013         return regex_;
1014       }
1015 
GetParameters() const1016       const _OrthancPluginChunkedRestCallback& GetParameters() const
1017       {
1018         return parameters_;
1019       }
1020     };
1021 
1022 
1023 
1024     class StorageCommitmentScp : public IStorageCommitmentFactory
1025     {
1026     private:
1027       class Handler : public IStorageCommitmentFactory::ILookupHandler
1028       {
1029       private:
1030         _OrthancPluginRegisterStorageCommitmentScpCallback  parameters_;
1031         void*    handler_;
1032 
1033       public:
Handler(const _OrthancPluginRegisterStorageCommitmentScpCallback & parameters,void * handler)1034         Handler(const _OrthancPluginRegisterStorageCommitmentScpCallback& parameters,
1035                 void* handler) :
1036           parameters_(parameters),
1037           handler_(handler)
1038         {
1039           if (handler == NULL)
1040           {
1041             throw OrthancException(ErrorCode_NullPointer);
1042           }
1043         }
1044 
~Handler()1045         virtual ~Handler()
1046         {
1047           assert(handler_ != NULL);
1048           parameters_.destructor(handler_);
1049           handler_ = NULL;
1050         }
1051 
Lookup(const std::string & sopClassUid,const std::string & sopInstanceUid)1052         virtual StorageCommitmentFailureReason Lookup(
1053           const std::string& sopClassUid,
1054           const std::string& sopInstanceUid) ORTHANC_OVERRIDE
1055         {
1056           assert(handler_ != NULL);
1057           OrthancPluginStorageCommitmentFailureReason reason =
1058             OrthancPluginStorageCommitmentFailureReason_Success;
1059           OrthancPluginErrorCode error = parameters_.lookup(
1060             &reason, handler_, sopClassUid.c_str(), sopInstanceUid.c_str());
1061           if (error == OrthancPluginErrorCode_Success)
1062           {
1063             return Plugins::Convert(reason);
1064           }
1065           else
1066           {
1067             throw OrthancException(static_cast<ErrorCode>(error));
1068           }
1069         }
1070       };
1071 
1072       _OrthancPluginRegisterStorageCommitmentScpCallback  parameters_;
1073 
1074     public:
StorageCommitmentScp(const _OrthancPluginRegisterStorageCommitmentScpCallback & parameters)1075       explicit StorageCommitmentScp(const _OrthancPluginRegisterStorageCommitmentScpCallback& parameters) :
1076         parameters_(parameters)
1077       {
1078       }
1079 
CreateStorageCommitment(const std::string & jobId,const std::string & transactionUid,const std::vector<std::string> & sopClassUids,const std::vector<std::string> & sopInstanceUids,const std::string & remoteAet,const std::string & calledAet)1080       virtual ILookupHandler* CreateStorageCommitment(
1081         const std::string& jobId,
1082         const std::string& transactionUid,
1083         const std::vector<std::string>& sopClassUids,
1084         const std::vector<std::string>& sopInstanceUids,
1085         const std::string& remoteAet,
1086         const std::string& calledAet) ORTHANC_OVERRIDE
1087       {
1088         const size_t n = sopClassUids.size();
1089 
1090         if (sopInstanceUids.size() != n)
1091         {
1092           throw OrthancException(ErrorCode_ParameterOutOfRange);
1093         }
1094 
1095         std::vector<const char*> a, b;
1096         a.resize(n);
1097         b.resize(n);
1098 
1099         for (size_t i = 0; i < n; i++)
1100         {
1101           a[i] = sopClassUids[i].c_str();
1102           b[i] = sopInstanceUids[i].c_str();
1103         }
1104 
1105         void* handler = NULL;
1106         OrthancPluginErrorCode error = parameters_.factory(
1107           &handler, jobId.c_str(), transactionUid.c_str(),
1108           a.empty() ? NULL : &a[0], b.empty() ? NULL : &b[0], static_cast<uint32_t>(n),
1109           remoteAet.c_str(), calledAet.c_str());
1110 
1111         if (error != OrthancPluginErrorCode_Success)
1112         {
1113           throw OrthancException(static_cast<ErrorCode>(error));
1114         }
1115         else if (handler == NULL)
1116         {
1117           // This plugin won't handle this storage commitment request
1118           return NULL;
1119         }
1120         else
1121         {
1122           return new Handler(parameters_, handler);
1123         }
1124       }
1125     };
1126 
1127 
1128     class ServerContextLock
1129     {
1130     private:
1131       boost::mutex::scoped_lock  lock_;
1132       ServerContext* context_;
1133 
1134     public:
ServerContextLock(PImpl & that)1135       explicit ServerContextLock(PImpl& that) :
1136         lock_(that.contextMutex_),
1137         context_(that.context_)
1138       {
1139         if (context_ == NULL)
1140         {
1141           throw OrthancException(ErrorCode_DatabaseNotInitialized);
1142         }
1143       }
1144 
GetContext()1145       ServerContext& GetContext()
1146       {
1147         assert(context_ != NULL);
1148         return *context_;
1149       }
1150     };
1151 
1152 
SetServerContext(ServerContext * context)1153     void SetServerContext(ServerContext* context)
1154     {
1155       boost::mutex::scoped_lock lock(contextMutex_);
1156       context_ = context;
1157     }
1158 
1159 
1160     typedef std::pair<std::string, _OrthancPluginProperty>  Property;
1161     typedef std::list<RestCallback*>  RestCallbacks;
1162     typedef std::list<ChunkedRestCallback*>  ChunkedRestCallbacks;
1163     typedef std::list<OrthancPluginOnStoredInstanceCallback>  OnStoredCallbacks;
1164     typedef std::list<OrthancPluginOnChangeCallback>  OnChangeCallbacks;
1165     typedef std::list<OrthancPluginIncomingHttpRequestFilter>  IncomingHttpRequestFilters;
1166     typedef std::list<OrthancPluginIncomingHttpRequestFilter2>  IncomingHttpRequestFilters2;
1167     typedef std::list<OrthancPluginIncomingDicomInstanceFilter>  IncomingDicomInstanceFilters;
1168     typedef std::list<OrthancPluginDecodeImageCallback>  DecodeImageCallbacks;
1169     typedef std::list<OrthancPluginTranscoderCallback>  TranscoderCallbacks;
1170     typedef std::list<OrthancPluginJobsUnserializer>  JobsUnserializers;
1171     typedef std::list<OrthancPluginRefreshMetricsCallback>  RefreshMetricsCallbacks;
1172     typedef std::list<StorageCommitmentScp*>  StorageCommitmentScpCallbacks;
1173     typedef std::map<Property, std::string>  Properties;
1174 
1175     PluginsManager manager_;
1176 
1177     RestCallbacks restCallbacks_;
1178     ChunkedRestCallbacks chunkedRestCallbacks_;
1179     OnStoredCallbacks  onStoredCallbacks_;
1180     OnChangeCallbacks  onChangeCallbacks_;
1181     OrthancPluginFindCallback  findCallback_;
1182     OrthancPluginWorklistCallback  worklistCallback_;
1183     DecodeImageCallbacks  decodeImageCallbacks_;
1184     TranscoderCallbacks  transcoderCallbacks_;
1185     JobsUnserializers  jobsUnserializers_;
1186     _OrthancPluginMoveCallback moveCallbacks_;
1187     IncomingHttpRequestFilters  incomingHttpRequestFilters_;
1188     IncomingHttpRequestFilters2 incomingHttpRequestFilters2_;
1189     IncomingDicomInstanceFilters  incomingDicomInstanceFilters_;
1190     RefreshMetricsCallbacks refreshMetricsCallbacks_;
1191     StorageCommitmentScpCallbacks storageCommitmentScpCallbacks_;
1192     std::unique_ptr<StorageAreaFactory>  storageArea_;
1193     std::set<std::string> authorizationTokens_;
1194 
1195     boost::recursive_mutex restCallbackInvokationMutex_;
1196     boost::shared_mutex restCallbackRegistrationMutex_;  // New in Orthanc 1.9.0
1197     boost::recursive_mutex storedCallbackMutex_;
1198     boost::recursive_mutex changeCallbackMutex_;
1199     boost::mutex findCallbackMutex_;
1200     boost::mutex worklistCallbackMutex_;
1201     boost::shared_mutex decoderTranscoderMutex_;  // Changed from "boost::mutex" in Orthanc 1.7.0
1202     boost::mutex jobsUnserializersMutex_;
1203     boost::mutex refreshMetricsMutex_;
1204     boost::mutex storageCommitmentScpMutex_;
1205     boost::recursive_mutex invokeServiceMutex_;
1206     boost::shared_mutex incomingHttpRequestFilterMutex_;  // New in Orthanc 1.8.2
1207 
1208     Properties properties_;
1209     int argc_;
1210     char** argv_;
1211     std::unique_ptr<OrthancPluginDatabase>  database_;
1212     std::unique_ptr<OrthancPluginDatabaseV3>  databaseV3_;  // New in Orthanc 1.9.2
1213     PluginsErrorDictionary  dictionary_;
1214     std::string databaseServerIdentifier_;   // New in Orthanc 1.9.2
1215     unsigned int maxDatabaseRetries_;   // New in Orthanc 1.9.2
1216 
PImpl(const std::string & databaseServerIdentifier)1217     explicit PImpl(const std::string& databaseServerIdentifier) :
1218       context_(NULL),
1219       findCallback_(NULL),
1220       worklistCallback_(NULL),
1221       argc_(1),
1222       argv_(NULL),
1223       databaseServerIdentifier_(databaseServerIdentifier),
1224       maxDatabaseRetries_(0)
1225     {
1226       memset(&moveCallbacks_, 0, sizeof(moveCallbacks_));
1227     }
1228   };
1229 
1230 
1231 
1232   class OrthancPlugins::WorklistHandler : public IWorklistRequestHandler
1233   {
1234   private:
1235     OrthancPlugins&  that_;
1236     std::unique_ptr<HierarchicalMatcher> matcher_;
1237     std::unique_ptr<ParsedDicomFile>     filtered_;
1238     ParsedDicomFile* currentQuery_;
1239 
Reset()1240     void Reset()
1241     {
1242       matcher_.reset();
1243       filtered_.reset();
1244       currentQuery_ = NULL;
1245     }
1246 
1247   public:
WorklistHandler(OrthancPlugins & that)1248     explicit WorklistHandler(OrthancPlugins& that) : that_(that)
1249     {
1250       Reset();
1251     }
1252 
Handle(DicomFindAnswers & answers,ParsedDicomFile & query,const std::string & remoteIp,const std::string & remoteAet,const std::string & calledAet,ModalityManufacturer manufacturer)1253     virtual void Handle(DicomFindAnswers& answers,
1254                         ParsedDicomFile& query,
1255                         const std::string& remoteIp,
1256                         const std::string& remoteAet,
1257                         const std::string& calledAet,
1258                         ModalityManufacturer manufacturer) ORTHANC_OVERRIDE
1259     {
1260       {
1261         static const char* LUA_CALLBACK = "IncomingWorklistRequestFilter";
1262 
1263         PImpl::ServerContextLock lock(*that_.pimpl_);
1264         LuaScripting::Lock lua(lock.GetContext().GetLuaScripting());
1265 
1266         if (!lua.GetLua().IsExistingFunction(LUA_CALLBACK))
1267         {
1268           currentQuery_ = &query;
1269         }
1270         else
1271         {
1272           Json::Value source, origin;
1273           query.DatasetToJson(source, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
1274 
1275           OrthancFindRequestHandler::FormatOrigin
1276             (origin, remoteIp, remoteAet, calledAet, manufacturer);
1277 
1278           LuaFunctionCall call(lua.GetLua(), LUA_CALLBACK);
1279           call.PushJson(source);
1280           call.PushJson(origin);
1281 
1282           Json::Value target;
1283           call.ExecuteToJson(target, true);
1284 
1285           filtered_.reset(ParsedDicomFile::CreateFromJson(target, DicomFromJsonFlags_None,
1286                                                           "" /* no private creator */));
1287           currentQuery_ = filtered_.get();
1288         }
1289       }
1290 
1291       matcher_.reset(new HierarchicalMatcher(*currentQuery_));
1292 
1293       {
1294         boost::mutex::scoped_lock lock(that_.pimpl_->worklistCallbackMutex_);
1295 
1296         if (that_.pimpl_->worklistCallback_)
1297         {
1298           OrthancPluginErrorCode error = that_.pimpl_->worklistCallback_
1299             (reinterpret_cast<OrthancPluginWorklistAnswers*>(&answers),
1300              reinterpret_cast<const OrthancPluginWorklistQuery*>(this),
1301              remoteAet.c_str(),
1302              calledAet.c_str());
1303 
1304           if (error != OrthancPluginErrorCode_Success)
1305           {
1306             Reset();
1307             that_.GetErrorDictionary().LogError(error, true);
1308             throw OrthancException(static_cast<ErrorCode>(error));
1309           }
1310         }
1311 
1312         Reset();
1313       }
1314     }
1315 
GetDicomQuery(OrthancPluginMemoryBuffer & target) const1316     void GetDicomQuery(OrthancPluginMemoryBuffer& target) const
1317     {
1318       if (currentQuery_ == NULL)
1319       {
1320         throw OrthancException(ErrorCode_Plugin);
1321       }
1322 
1323       std::string dicom;
1324       currentQuery_->SaveToMemoryBuffer(dicom);
1325       CopyToMemoryBuffer(target, dicom.c_str(), dicom.size());
1326     }
1327 
IsMatch(const void * dicom,size_t size) const1328     bool IsMatch(const void* dicom,
1329                  size_t size) const
1330     {
1331       if (matcher_.get() == NULL)
1332       {
1333         throw OrthancException(ErrorCode_Plugin);
1334       }
1335 
1336       ParsedDicomFile f(dicom, size);
1337       return matcher_->Match(f);
1338     }
1339 
AddAnswer(OrthancPluginWorklistAnswers * answers,const void * dicom,size_t size) const1340     void AddAnswer(OrthancPluginWorklistAnswers* answers,
1341                    const void* dicom,
1342                    size_t size) const
1343     {
1344       if (matcher_.get() == NULL)
1345       {
1346         throw OrthancException(ErrorCode_Plugin);
1347       }
1348 
1349       ParsedDicomFile f(dicom, size);
1350       std::unique_ptr<ParsedDicomFile> summary(matcher_->Extract(f));
1351       reinterpret_cast<DicomFindAnswers*>(answers)->Add(*summary);
1352     }
1353   };
1354 
1355 
1356   class OrthancPlugins::FindHandler : public IFindRequestHandler
1357   {
1358   private:
1359     OrthancPlugins&            that_;
1360     std::unique_ptr<DicomArray>  currentQuery_;
1361 
Reset()1362     void Reset()
1363     {
1364       currentQuery_.reset(NULL);
1365     }
1366 
1367   public:
FindHandler(OrthancPlugins & that)1368     explicit FindHandler(OrthancPlugins& that) : that_(that)
1369     {
1370       Reset();
1371     }
1372 
Handle(DicomFindAnswers & answers,const DicomMap & input,const std::list<DicomTag> & sequencesToReturn,const std::string & remoteIp,const std::string & remoteAet,const std::string & calledAet,ModalityManufacturer manufacturer)1373     virtual void Handle(DicomFindAnswers& answers,
1374                         const DicomMap& input,
1375                         const std::list<DicomTag>& sequencesToReturn,
1376                         const std::string& remoteIp,
1377                         const std::string& remoteAet,
1378                         const std::string& calledAet,
1379                         ModalityManufacturer manufacturer) ORTHANC_OVERRIDE
1380     {
1381       DicomMap tmp;
1382       tmp.Assign(input);
1383 
1384       for (std::list<DicomTag>::const_iterator it = sequencesToReturn.begin();
1385            it != sequencesToReturn.end(); ++it)
1386       {
1387         if (!input.HasTag(*it))
1388         {
1389           tmp.SetValue(*it, "", false);
1390         }
1391       }
1392 
1393       {
1394         boost::mutex::scoped_lock lock(that_.pimpl_->findCallbackMutex_);
1395         currentQuery_.reset(new DicomArray(tmp));
1396 
1397         if (that_.pimpl_->findCallback_)
1398         {
1399           OrthancPluginErrorCode error = that_.pimpl_->findCallback_
1400             (reinterpret_cast<OrthancPluginFindAnswers*>(&answers),
1401              reinterpret_cast<const OrthancPluginFindQuery*>(this),
1402              remoteAet.c_str(),
1403              calledAet.c_str());
1404 
1405           if (error != OrthancPluginErrorCode_Success)
1406           {
1407             Reset();
1408             that_.GetErrorDictionary().LogError(error, true);
1409             throw OrthancException(static_cast<ErrorCode>(error));
1410           }
1411         }
1412 
1413         Reset();
1414       }
1415     }
1416 
Invoke(_OrthancPluginService service,const _OrthancPluginFindOperation & operation) const1417     void Invoke(_OrthancPluginService service,
1418                 const _OrthancPluginFindOperation& operation) const
1419     {
1420       if (currentQuery_.get() == NULL)
1421       {
1422         throw OrthancException(ErrorCode_Plugin);
1423       }
1424 
1425       switch (service)
1426       {
1427         case _OrthancPluginService_GetFindQuerySize:
1428           *operation.resultUint32 = currentQuery_->GetSize();
1429           break;
1430 
1431         case _OrthancPluginService_GetFindQueryTag:
1432         {
1433           const DicomTag& tag = currentQuery_->GetElement(operation.index).GetTag();
1434           *operation.resultGroup = tag.GetGroup();
1435           *operation.resultElement = tag.GetElement();
1436           break;
1437         }
1438 
1439         case _OrthancPluginService_GetFindQueryTagName:
1440         {
1441           const DicomElement& element = currentQuery_->GetElement(operation.index);
1442           *operation.resultString = CopyString(FromDcmtkBridge::GetTagName(element));
1443           break;
1444         }
1445 
1446         case _OrthancPluginService_GetFindQueryValue:
1447         {
1448           *operation.resultString = CopyString(currentQuery_->GetElement(operation.index).GetValue().GetContent());
1449           break;
1450         }
1451 
1452         default:
1453           throw OrthancException(ErrorCode_InternalError);
1454       }
1455     }
1456   };
1457 
1458 
1459 
1460   class OrthancPlugins::MoveHandler : public IMoveRequestHandler
1461   {
1462   private:
1463     class Driver : public IMoveRequestIterator
1464     {
1465     private:
1466       void*                   driver_;
1467       unsigned int            count_;
1468       unsigned int            pos_;
1469       OrthancPluginApplyMove  apply_;
1470       OrthancPluginFreeMove   free_;
1471 
1472     public:
Driver(void * driver,unsigned int count,OrthancPluginApplyMove apply,OrthancPluginFreeMove free)1473       Driver(void* driver,
1474              unsigned int count,
1475              OrthancPluginApplyMove apply,
1476              OrthancPluginFreeMove free) :
1477         driver_(driver),
1478         count_(count),
1479         pos_(0),
1480         apply_(apply),
1481         free_(free)
1482       {
1483         if (driver_ == NULL)
1484         {
1485           throw OrthancException(ErrorCode_Plugin);
1486         }
1487       }
1488 
~Driver()1489       virtual ~Driver()
1490       {
1491         if (driver_ != NULL)
1492         {
1493           free_(driver_);
1494           driver_ = NULL;
1495         }
1496       }
1497 
GetSubOperationCount() const1498       virtual unsigned int GetSubOperationCount() const ORTHANC_OVERRIDE
1499       {
1500         return count_;
1501       }
1502 
DoNext()1503       virtual Status DoNext() ORTHANC_OVERRIDE
1504       {
1505         if (pos_ >= count_)
1506         {
1507           throw OrthancException(ErrorCode_BadSequenceOfCalls);
1508         }
1509         else
1510         {
1511           OrthancPluginErrorCode error = apply_(driver_);
1512           if (error != OrthancPluginErrorCode_Success)
1513           {
1514             LOG(ERROR) << "Error while doing C-Move from plugin: "
1515                        << EnumerationToString(static_cast<ErrorCode>(error));
1516             return Status_Failure;
1517           }
1518           else
1519           {
1520             pos_++;
1521             return Status_Success;
1522           }
1523         }
1524       }
1525     };
1526 
1527 
1528     _OrthancPluginMoveCallback  params_;
1529 
1530 
ReadTag(const DicomMap & input,const DicomTag & tag)1531     static std::string ReadTag(const DicomMap& input,
1532                                const DicomTag& tag)
1533     {
1534       const DicomValue* value = input.TestAndGetValue(tag);
1535       if (value != NULL &&
1536           !value->IsBinary() &&
1537           !value->IsNull())
1538       {
1539         return value->GetContent();
1540       }
1541       else
1542       {
1543         return std::string();
1544       }
1545     }
1546 
1547 
1548 
1549   public:
MoveHandler(OrthancPlugins & that)1550     explicit MoveHandler(OrthancPlugins& that)
1551     {
1552       boost::recursive_mutex::scoped_lock lock(that.pimpl_->invokeServiceMutex_);
1553       params_ = that.pimpl_->moveCallbacks_;
1554 
1555       if (params_.callback == NULL ||
1556           params_.getMoveSize == NULL ||
1557           params_.applyMove == NULL ||
1558           params_.freeMove == NULL)
1559       {
1560         throw OrthancException(ErrorCode_Plugin);
1561       }
1562     }
1563 
Handle(const std::string & targetAet,const DicomMap & input,const std::string & originatorIp,const std::string & originatorAet,const std::string & calledAet,uint16_t originatorId)1564     virtual IMoveRequestIterator* Handle(const std::string& targetAet,
1565                                          const DicomMap& input,
1566                                          const std::string& originatorIp,
1567                                          const std::string& originatorAet,
1568                                          const std::string& calledAet,
1569                                          uint16_t originatorId) ORTHANC_OVERRIDE
1570     {
1571       std::string levelString = ReadTag(input, DICOM_TAG_QUERY_RETRIEVE_LEVEL);
1572       std::string patientId = ReadTag(input, DICOM_TAG_PATIENT_ID);
1573       std::string accessionNumber = ReadTag(input, DICOM_TAG_ACCESSION_NUMBER);
1574       std::string studyInstanceUid = ReadTag(input, DICOM_TAG_STUDY_INSTANCE_UID);
1575       std::string seriesInstanceUid = ReadTag(input, DICOM_TAG_SERIES_INSTANCE_UID);
1576       std::string sopInstanceUid = ReadTag(input, DICOM_TAG_SOP_INSTANCE_UID);
1577 
1578       OrthancPluginResourceType level = OrthancPluginResourceType_None;
1579 
1580       if (!levelString.empty())
1581       {
1582         level = Plugins::Convert(StringToResourceType(levelString.c_str()));
1583       }
1584 
1585       void* driver = params_.callback(level,
1586                                       patientId.empty() ? NULL : patientId.c_str(),
1587                                       accessionNumber.empty() ? NULL : accessionNumber.c_str(),
1588                                       studyInstanceUid.empty() ? NULL : studyInstanceUid.c_str(),
1589                                       seriesInstanceUid.empty() ? NULL : seriesInstanceUid.c_str(),
1590                                       sopInstanceUid.empty() ? NULL : sopInstanceUid.c_str(),
1591                                       originatorAet.c_str(),
1592                                       calledAet.c_str(),
1593                                       targetAet.c_str(),
1594                                       originatorId);
1595 
1596       if (driver == NULL)
1597       {
1598         throw OrthancException(ErrorCode_Plugin,
1599                                "Plugin cannot create a driver for an incoming C-MOVE request");
1600       }
1601 
1602       unsigned int size = params_.getMoveSize(driver);
1603 
1604       return new Driver(driver, size, params_.applyMove, params_.freeMove);
1605     }
1606   };
1607 
1608 
1609 
1610   class OrthancPlugins::HttpClientChunkedRequest : public HttpClient::IRequestBody
1611   {
1612   private:
1613     const _OrthancPluginChunkedHttpClient&  params_;
1614     PluginsErrorDictionary&                 errorDictionary_;
1615 
1616   public:
HttpClientChunkedRequest(const _OrthancPluginChunkedHttpClient & params,PluginsErrorDictionary & errorDictionary)1617     HttpClientChunkedRequest(const _OrthancPluginChunkedHttpClient& params,
1618                              PluginsErrorDictionary&  errorDictionary) :
1619       params_(params),
1620       errorDictionary_(errorDictionary)
1621     {
1622     }
1623 
ReadNextChunk(std::string & chunk)1624     virtual bool ReadNextChunk(std::string& chunk) ORTHANC_OVERRIDE
1625     {
1626       if (params_.requestIsDone(params_.request))
1627       {
1628         return false;
1629       }
1630       else
1631       {
1632         size_t size = params_.requestChunkSize(params_.request);
1633 
1634         chunk.resize(size);
1635 
1636         if (size != 0)
1637         {
1638           const void* data = params_.requestChunkData(params_.request);
1639           memcpy(&chunk[0], data, size);
1640         }
1641 
1642         OrthancPluginErrorCode error = params_.requestNext(params_.request);
1643 
1644         if (error != OrthancPluginErrorCode_Success)
1645         {
1646           errorDictionary_.LogError(error, true);
1647           throw OrthancException(static_cast<ErrorCode>(error));
1648         }
1649         else
1650         {
1651           return true;
1652         }
1653       }
1654     }
1655   };
1656 
1657 
1658   class OrthancPlugins::HttpClientChunkedAnswer : public HttpClient::IAnswer
1659   {
1660   private:
1661     const _OrthancPluginChunkedHttpClient&  params_;
1662     PluginsErrorDictionary&                 errorDictionary_;
1663 
1664   public:
HttpClientChunkedAnswer(const _OrthancPluginChunkedHttpClient & params,PluginsErrorDictionary & errorDictionary)1665     HttpClientChunkedAnswer(const _OrthancPluginChunkedHttpClient& params,
1666                             PluginsErrorDictionary&  errorDictionary) :
1667       params_(params),
1668       errorDictionary_(errorDictionary)
1669     {
1670     }
1671 
AddHeader(const std::string & key,const std::string & value)1672     virtual void AddHeader(const std::string& key,
1673                            const std::string& value) ORTHANC_OVERRIDE
1674     {
1675       OrthancPluginErrorCode error = params_.answerAddHeader(params_.answer, key.c_str(), value.c_str());
1676 
1677       if (error != OrthancPluginErrorCode_Success)
1678       {
1679         errorDictionary_.LogError(error, true);
1680         throw OrthancException(static_cast<ErrorCode>(error));
1681       }
1682     }
1683 
AddChunk(const void * data,size_t size)1684     virtual void AddChunk(const void* data,
1685                           size_t size) ORTHANC_OVERRIDE
1686     {
1687       OrthancPluginErrorCode error = params_.answerAddChunk(params_.answer, data, size);
1688 
1689       if (error != OrthancPluginErrorCode_Success)
1690       {
1691         errorDictionary_.LogError(error, true);
1692         throw OrthancException(static_cast<ErrorCode>(error));
1693       }
1694     }
1695   };
1696 
1697 
OrthancPlugins(const std::string & databaseServerIdentifier)1698   OrthancPlugins::OrthancPlugins(const std::string& databaseServerIdentifier)
1699   {
1700     /* Sanity check of the compiler */
1701     if (sizeof(int32_t) != sizeof(OrthancPluginErrorCode) ||
1702         sizeof(int32_t) != sizeof(OrthancPluginHttpMethod) ||
1703         sizeof(int32_t) != sizeof(_OrthancPluginService) ||
1704         sizeof(int32_t) != sizeof(_OrthancPluginProperty) ||
1705         sizeof(int32_t) != sizeof(OrthancPluginPixelFormat) ||
1706         sizeof(int32_t) != sizeof(OrthancPluginContentType) ||
1707         sizeof(int32_t) != sizeof(OrthancPluginResourceType) ||
1708         sizeof(int32_t) != sizeof(OrthancPluginChangeType) ||
1709         sizeof(int32_t) != sizeof(OrthancPluginImageFormat) ||
1710         sizeof(int32_t) != sizeof(OrthancPluginCompressionType) ||
1711         sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation) ||
1712         sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFlags) ||
1713         sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFormat) ||
1714         sizeof(int32_t) != sizeof(OrthancPluginCreateDicomFlags) ||
1715         sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType) ||
1716         sizeof(int32_t) != sizeof(OrthancPluginIdentifierConstraint) ||
1717         sizeof(int32_t) != sizeof(OrthancPluginInstanceOrigin) ||
1718         sizeof(int32_t) != sizeof(OrthancPluginJobStepStatus) ||
1719         sizeof(int32_t) != sizeof(OrthancPluginConstraintType) ||
1720         sizeof(int32_t) != sizeof(OrthancPluginMetricsType) ||
1721         sizeof(int32_t) != sizeof(OrthancPluginDicomWebBinaryMode) ||
1722         sizeof(int32_t) != sizeof(OrthancPluginStorageCommitmentFailureReason) ||
1723         static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeBinary) != static_cast<int>(DicomToJsonFlags_IncludeBinary) ||
1724         static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePrivateTags) != static_cast<int>(DicomToJsonFlags_IncludePrivateTags) ||
1725         static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeUnknownTags) != static_cast<int>(DicomToJsonFlags_IncludeUnknownTags) ||
1726         static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePixelData) != static_cast<int>(DicomToJsonFlags_IncludePixelData) ||
1727         static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToNull) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToNull) ||
1728         static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToAscii) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToAscii) ||
1729         static_cast<int>(OrthancPluginDicomToJsonFlags_StopAfterPixelData) != static_cast<int>(DicomToJsonFlags_StopAfterPixelData) ||
1730         static_cast<int>(OrthancPluginDicomToJsonFlags_SkipGroupLengths) != static_cast<int>(DicomToJsonFlags_SkipGroupLengths) ||
1731         static_cast<int>(OrthancPluginCreateDicomFlags_DecodeDataUriScheme) != static_cast<int>(DicomFromJsonFlags_DecodeDataUriScheme) ||
1732         static_cast<int>(OrthancPluginCreateDicomFlags_GenerateIdentifiers) != static_cast<int>(DicomFromJsonFlags_GenerateIdentifiers))
1733 
1734     {
1735       throw OrthancException(ErrorCode_Plugin);
1736     }
1737 
1738     pimpl_.reset(new PImpl(databaseServerIdentifier));
1739     pimpl_->manager_.RegisterServiceProvider(*this);
1740   }
1741 
1742 
SetServerContext(ServerContext & context)1743   void OrthancPlugins::SetServerContext(ServerContext& context)
1744   {
1745     pimpl_->SetServerContext(&context);
1746   }
1747 
1748 
ResetServerContext()1749   void OrthancPlugins::ResetServerContext()
1750   {
1751     pimpl_->SetServerContext(NULL);
1752   }
1753 
1754 
~OrthancPlugins()1755   OrthancPlugins::~OrthancPlugins()
1756   {
1757     for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin();
1758          it != pimpl_->restCallbacks_.end(); ++it)
1759     {
1760       delete *it;
1761     }
1762 
1763     for (PImpl::ChunkedRestCallbacks::iterator it = pimpl_->chunkedRestCallbacks_.begin();
1764          it != pimpl_->chunkedRestCallbacks_.end(); ++it)
1765     {
1766       delete *it;
1767     }
1768 
1769     for (PImpl::StorageCommitmentScpCallbacks::iterator
1770            it = pimpl_->storageCommitmentScpCallbacks_.begin();
1771          it != pimpl_->storageCommitmentScpCallbacks_.end(); ++it)
1772     {
1773       delete *it;
1774     }
1775   }
1776 
1777 
ArgumentsToPlugin(std::vector<const char * > & keys,std::vector<const char * > & values,const HttpToolbox::Arguments & arguments)1778   static void ArgumentsToPlugin(std::vector<const char*>& keys,
1779                                 std::vector<const char*>& values,
1780                                 const HttpToolbox::Arguments& arguments)
1781   {
1782     keys.resize(arguments.size());
1783     values.resize(arguments.size());
1784 
1785     size_t pos = 0;
1786     for (HttpToolbox::Arguments::const_iterator
1787            it = arguments.begin(); it != arguments.end(); ++it)
1788     {
1789       keys[pos] = it->first.c_str();
1790       values[pos] = it->second.c_str();
1791       pos++;
1792     }
1793   }
1794 
1795 
ArgumentsToPlugin(std::vector<const char * > & keys,std::vector<const char * > & values,const HttpToolbox::GetArguments & arguments)1796   static void ArgumentsToPlugin(std::vector<const char*>& keys,
1797                                 std::vector<const char*>& values,
1798                                 const HttpToolbox::GetArguments& arguments)
1799   {
1800     keys.resize(arguments.size());
1801     values.resize(arguments.size());
1802 
1803     for (size_t i = 0; i < arguments.size(); i++)
1804     {
1805       keys[i] = arguments[i].first.c_str();
1806       values[i] = arguments[i].second.c_str();
1807     }
1808   }
1809 
1810 
1811   namespace
1812   {
1813     class RestCallbackMatcher : public boost::noncopyable
1814     {
1815     private:
1816       std::string               flatUri_;
1817       std::vector<std::string>  groups_;
1818       std::vector<const char*>  cgroups_;
1819 
1820     public:
RestCallbackMatcher(const UriComponents & uri)1821       explicit RestCallbackMatcher(const UriComponents& uri) :
1822         flatUri_(Toolbox::FlattenUri(uri))
1823       {
1824       }
1825 
IsMatch(const boost::regex & re)1826       bool IsMatch(const boost::regex& re)
1827       {
1828         // Check whether the regular expression associated to this
1829         // callback matches the URI
1830         boost::cmatch what;
1831 
1832         if (boost::regex_match(flatUri_.c_str(), what, re))
1833         {
1834           // Extract the value of the free parameters of the regular expression
1835           if (what.size() > 1)
1836           {
1837             groups_.resize(what.size() - 1);
1838             cgroups_.resize(what.size() - 1);
1839             for (size_t i = 1; i < what.size(); i++)
1840             {
1841               groups_[i - 1] = what[i];
1842               cgroups_[i - 1] = groups_[i - 1].c_str();
1843             }
1844           }
1845 
1846           return true;
1847         }
1848         else
1849         {
1850           // Not a match
1851           return false;
1852         }
1853       }
1854 
GetGroupsCount() const1855       uint32_t GetGroupsCount() const
1856       {
1857         return cgroups_.size();
1858       }
1859 
GetGroups() const1860       const char* const* GetGroups() const
1861       {
1862         return cgroups_.empty() ? NULL : &cgroups_[0];
1863       }
1864 
GetFlatUri() const1865       const std::string& GetFlatUri() const
1866       {
1867         return flatUri_;
1868       }
1869     };
1870 
1871 
1872     // WARNING - The lifetime of this internal object must be smaller
1873     // than "matcher", "headers" and "getArguments" objects
1874     class HttpRequestConverter
1875     {
1876     private:
1877       std::vector<const char*>    getKeys_;
1878       std::vector<const char*>    getValues_;
1879       std::vector<const char*>    headersKeys_;
1880       std::vector<const char*>    headersValues_;
1881       OrthancPluginHttpRequest    converted_;
1882 
1883     public:
HttpRequestConverter(const RestCallbackMatcher & matcher,HttpMethod method,const HttpToolbox::Arguments & headers)1884       HttpRequestConverter(const RestCallbackMatcher& matcher,
1885                            HttpMethod method,
1886                            const HttpToolbox::Arguments& headers)
1887       {
1888         memset(&converted_, 0, sizeof(OrthancPluginHttpRequest));
1889 
1890         ArgumentsToPlugin(headersKeys_, headersValues_, headers);
1891         assert(headersKeys_.size() == headersValues_.size());
1892 
1893         switch (method)
1894         {
1895           case HttpMethod_Get:
1896             converted_.method = OrthancPluginHttpMethod_Get;
1897             break;
1898 
1899           case HttpMethod_Post:
1900             converted_.method = OrthancPluginHttpMethod_Post;
1901             break;
1902 
1903           case HttpMethod_Delete:
1904             converted_.method = OrthancPluginHttpMethod_Delete;
1905             break;
1906 
1907           case HttpMethod_Put:
1908             converted_.method = OrthancPluginHttpMethod_Put;
1909             break;
1910 
1911           default:
1912             throw OrthancException(ErrorCode_InternalError);
1913         }
1914 
1915         converted_.groups = matcher.GetGroups();
1916         converted_.groupsCount = matcher.GetGroupsCount();
1917         converted_.getCount = 0;
1918         converted_.getKeys = NULL;
1919         converted_.getValues = NULL;
1920         converted_.body = NULL;
1921         converted_.bodySize = 0;
1922         converted_.headersCount = headers.size();
1923 
1924         if (headers.size() > 0)
1925         {
1926           converted_.headersKeys = &headersKeys_[0];
1927           converted_.headersValues = &headersValues_[0];
1928         }
1929       }
1930 
SetGetArguments(const HttpToolbox::GetArguments & getArguments)1931       void SetGetArguments(const HttpToolbox::GetArguments& getArguments)
1932       {
1933         ArgumentsToPlugin(getKeys_, getValues_, getArguments);
1934         assert(getKeys_.size() == getValues_.size());
1935 
1936         converted_.getCount = getArguments.size();
1937 
1938         if (getArguments.size() > 0)
1939         {
1940           converted_.getKeys = &getKeys_[0];
1941           converted_.getValues = &getValues_[0];
1942         }
1943       }
1944 
GetRequest()1945       OrthancPluginHttpRequest& GetRequest()
1946       {
1947         return converted_;
1948       }
1949     };
1950   }
1951 
1952 
GetAllowedMethods(_OrthancPluginChunkedRestCallback parameters)1953   static std::string GetAllowedMethods(_OrthancPluginChunkedRestCallback parameters)
1954   {
1955     std::string s;
1956 
1957     if (parameters.getHandler != NULL)
1958     {
1959       s += "GET";
1960     }
1961 
1962     if (parameters.postHandler != NULL)
1963     {
1964       if (!s.empty())
1965       {
1966         s+= ",";
1967       }
1968 
1969       s += "POST";
1970     }
1971 
1972     if (parameters.deleteHandler != NULL)
1973     {
1974       if (!s.empty())
1975       {
1976         s+= ",";
1977       }
1978 
1979       s += "DELETE";
1980     }
1981 
1982     if (parameters.putHandler != NULL)
1983     {
1984       if (!s.empty())
1985       {
1986         s+= ",";
1987       }
1988 
1989       s += "PUT";
1990     }
1991 
1992     return s;
1993   }
1994 
1995 
HandleChunkedGetDelete(HttpOutput & output,HttpMethod method,const UriComponents & uri,const HttpToolbox::Arguments & headers,const HttpToolbox::GetArguments & getArguments)1996   bool OrthancPlugins::HandleChunkedGetDelete(HttpOutput& output,
1997                                               HttpMethod method,
1998                                               const UriComponents& uri,
1999                                               const HttpToolbox::Arguments& headers,
2000                                               const HttpToolbox::GetArguments& getArguments)
2001   {
2002     RestCallbackMatcher matcher(uri);
2003 
2004     PImpl::ChunkedRestCallback* callback = NULL;
2005 
2006     // Loop over the callbacks registered by the plugins
2007     boost::shared_lock<boost::shared_mutex> lock(pimpl_->restCallbackRegistrationMutex_);
2008     for (PImpl::ChunkedRestCallbacks::const_iterator it = pimpl_->chunkedRestCallbacks_.begin();
2009          it != pimpl_->chunkedRestCallbacks_.end(); ++it)
2010     {
2011       if (matcher.IsMatch((*it)->GetRegularExpression()))
2012       {
2013         callback = *it;
2014         break;
2015       }
2016     }
2017 
2018     if (callback == NULL)
2019     {
2020       return false;
2021     }
2022     else
2023     {
2024       CLOG(INFO, PLUGINS) << "Delegating HTTP request to plugin for URI: " << matcher.GetFlatUri();
2025 
2026       OrthancPluginRestCallback handler;
2027 
2028       switch (method)
2029       {
2030         case HttpMethod_Get:
2031           handler = callback->GetParameters().getHandler;
2032           break;
2033 
2034         case HttpMethod_Delete:
2035           handler = callback->GetParameters().deleteHandler;
2036           break;
2037 
2038         default:
2039           handler = NULL;
2040           break;
2041       }
2042 
2043       if (handler == NULL)
2044       {
2045         output.SendMethodNotAllowed(GetAllowedMethods(callback->GetParameters()));
2046       }
2047       else
2048       {
2049         HttpRequestConverter converter(matcher, method, headers);
2050         converter.SetGetArguments(getArguments);
2051 
2052         PImpl::PluginHttpOutput pluginOutput(output);
2053 
2054         OrthancPluginErrorCode error = handler(
2055           reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput),
2056           matcher.GetFlatUri().c_str(), &converter.GetRequest());
2057 
2058         pluginOutput.Close(error, GetErrorDictionary());
2059       }
2060 
2061       return true;
2062     }
2063   }
2064 
2065 
Handle(HttpOutput & output,RequestOrigin,const char *,const char *,HttpMethod method,const UriComponents & uri,const HttpToolbox::Arguments & headers,const HttpToolbox::GetArguments & getArguments,const void * bodyData,size_t bodySize)2066   bool OrthancPlugins::Handle(HttpOutput& output,
2067                               RequestOrigin /*origin*/,
2068                               const char* /*remoteIp*/,
2069                               const char* /*username*/,
2070                               HttpMethod method,
2071                               const UriComponents& uri,
2072                               const HttpToolbox::Arguments& headers,
2073                               const HttpToolbox::GetArguments& getArguments,
2074                               const void* bodyData,
2075                               size_t bodySize)
2076   {
2077     RestCallbackMatcher matcher(uri);
2078 
2079     PImpl::RestCallback* callback = NULL;
2080 
2081     // Loop over the callbacks registered by the plugins
2082     boost::shared_lock<boost::shared_mutex> lock(pimpl_->restCallbackRegistrationMutex_);
2083     for (PImpl::RestCallbacks::const_iterator it = pimpl_->restCallbacks_.begin();
2084          it != pimpl_->restCallbacks_.end(); ++it)
2085     {
2086       if (matcher.IsMatch((*it)->GetRegularExpression()))
2087       {
2088         callback = *it;
2089         break;
2090       }
2091     }
2092 
2093     if (callback == NULL)
2094     {
2095       // Callback not found, try to find a chunked callback
2096       return HandleChunkedGetDelete(output, method, uri, headers, getArguments);
2097     }
2098 
2099     CLOG(INFO, PLUGINS) << "Delegating HTTP request to plugin for URI: " << matcher.GetFlatUri();
2100 
2101     HttpRequestConverter converter(matcher, method, headers);
2102     converter.SetGetArguments(getArguments);
2103     converter.GetRequest().body = bodyData;
2104     converter.GetRequest().bodySize = bodySize;
2105 
2106     PImpl::PluginHttpOutput pluginOutput(output);
2107 
2108     assert(callback != NULL);
2109     OrthancPluginErrorCode error = callback->Invoke
2110       (pimpl_->restCallbackInvokationMutex_, pluginOutput, matcher.GetFlatUri(), converter.GetRequest());
2111 
2112     pluginOutput.Close(error, GetErrorDictionary());
2113     return true;
2114   }
2115 
2116 
2117   class OrthancPlugins::IDicomInstance : public boost::noncopyable
2118   {
2119   public:
~IDicomInstance()2120     virtual ~IDicomInstance()
2121     {
2122     }
2123 
2124     virtual bool CanBeFreed() const = 0;
2125 
2126     virtual const DicomInstanceToStore& GetInstance() const = 0;
2127   };
2128 
2129 
2130   class OrthancPlugins::DicomInstanceFromCallback : public IDicomInstance
2131   {
2132   private:
2133     const DicomInstanceToStore&  instance_;
2134 
2135   public:
DicomInstanceFromCallback(const DicomInstanceToStore & instance)2136     explicit DicomInstanceFromCallback(const DicomInstanceToStore& instance) :
2137       instance_(instance)
2138     {
2139     }
2140 
CanBeFreed() const2141     virtual bool CanBeFreed() const ORTHANC_OVERRIDE
2142     {
2143       return false;
2144     }
2145 
GetInstance() const2146     virtual const DicomInstanceToStore& GetInstance() const ORTHANC_OVERRIDE
2147     {
2148       return instance_;
2149     };
2150   };
2151 
2152 
2153   class OrthancPlugins::DicomInstanceFromBuffer : public IDicomInstance
2154   {
2155   private:
2156     std::string                            buffer_;
2157     std::unique_ptr<DicomInstanceToStore>  instance_;
2158 
2159   public:
DicomInstanceFromBuffer(const void * buffer,size_t size)2160     DicomInstanceFromBuffer(const void* buffer,
2161                             size_t size)
2162     {
2163       buffer_.assign(reinterpret_cast<const char*>(buffer), size);
2164 
2165       instance_.reset(DicomInstanceToStore::CreateFromBuffer(buffer_));
2166       instance_->SetOrigin(DicomInstanceOrigin::FromPlugins());
2167     }
2168 
CanBeFreed() const2169     virtual bool CanBeFreed() const ORTHANC_OVERRIDE
2170     {
2171       return true;
2172     }
2173 
GetInstance() const2174     virtual const DicomInstanceToStore& GetInstance() const ORTHANC_OVERRIDE
2175     {
2176       return *instance_;
2177     };
2178   };
2179 
2180 
2181   class OrthancPlugins::DicomInstanceFromTranscoded : public IDicomInstance
2182   {
2183   private:
2184     std::unique_ptr<ParsedDicomFile>       parsed_;
2185     std::unique_ptr<DicomInstanceToStore>  instance_;
2186 
2187   public:
DicomInstanceFromTranscoded(IDicomTranscoder::DicomImage & transcoded)2188     explicit DicomInstanceFromTranscoded(IDicomTranscoder::DicomImage& transcoded) :
2189       parsed_(transcoded.ReleaseAsParsedDicomFile())
2190     {
2191       if (parsed_.get() == NULL)
2192       {
2193         throw OrthancException(ErrorCode_InternalError);
2194       }
2195 
2196       instance_.reset(DicomInstanceToStore::CreateFromParsedDicomFile(*parsed_));
2197       instance_->SetOrigin(DicomInstanceOrigin::FromPlugins());
2198     }
2199 
CanBeFreed() const2200     virtual bool CanBeFreed() const ORTHANC_OVERRIDE
2201     {
2202       return true;
2203     }
2204 
GetInstance() const2205     virtual const DicomInstanceToStore& GetInstance() const ORTHANC_OVERRIDE
2206     {
2207       return *instance_;
2208     };
2209   };
2210 
2211 
SignalStoredInstance(const std::string & instanceId,const DicomInstanceToStore & instance,const Json::Value & simplifiedTags)2212   void OrthancPlugins::SignalStoredInstance(const std::string& instanceId,
2213                                             const DicomInstanceToStore& instance,
2214                                             const Json::Value& simplifiedTags)
2215   {
2216     DicomInstanceFromCallback wrapped(instance);
2217 
2218     boost::recursive_mutex::scoped_lock lock(pimpl_->storedCallbackMutex_);
2219 
2220     for (PImpl::OnStoredCallbacks::const_iterator
2221            callback = pimpl_->onStoredCallbacks_.begin();
2222          callback != pimpl_->onStoredCallbacks_.end(); ++callback)
2223     {
2224       OrthancPluginErrorCode error = (*callback) (
2225         reinterpret_cast<OrthancPluginDicomInstance*>(&wrapped),
2226         instanceId.c_str());
2227 
2228       if (error != OrthancPluginErrorCode_Success)
2229       {
2230         GetErrorDictionary().LogError(error, true);
2231         throw OrthancException(static_cast<ErrorCode>(error));
2232       }
2233     }
2234   }
2235 
2236 
FilterIncomingInstance(const DicomInstanceToStore & instance,const Json::Value & simplified)2237   bool OrthancPlugins::FilterIncomingInstance(const DicomInstanceToStore& instance,
2238                                               const Json::Value& simplified)
2239   {
2240     DicomInstanceFromCallback wrapped(instance);
2241 
2242     boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
2243 
2244     for (PImpl::IncomingDicomInstanceFilters::const_iterator
2245            filter = pimpl_->incomingDicomInstanceFilters_.begin();
2246          filter != pimpl_->incomingDicomInstanceFilters_.end(); ++filter)
2247     {
2248       int32_t allowed = (*filter) (reinterpret_cast<const OrthancPluginDicomInstance*>(&wrapped));
2249 
2250       if (allowed == 0)
2251       {
2252         return false;
2253       }
2254       else if (allowed != 1)
2255       {
2256         // The callback is only allowed to answer 0 or 1
2257         throw OrthancException(ErrorCode_Plugin);
2258       }
2259     }
2260 
2261     return true;
2262   }
2263 
2264 
SignalChangeInternal(OrthancPluginChangeType changeType,OrthancPluginResourceType resourceType,const char * resource)2265   void OrthancPlugins::SignalChangeInternal(OrthancPluginChangeType changeType,
2266                                             OrthancPluginResourceType resourceType,
2267                                             const char* resource)
2268   {
2269     boost::recursive_mutex::scoped_lock lock(pimpl_->changeCallbackMutex_);
2270 
2271     for (std::list<OrthancPluginOnChangeCallback>::const_iterator
2272            callback = pimpl_->onChangeCallbacks_.begin();
2273          callback != pimpl_->onChangeCallbacks_.end(); ++callback)
2274     {
2275       OrthancPluginErrorCode error = (*callback) (changeType, resourceType, resource);
2276 
2277       if (error != OrthancPluginErrorCode_Success)
2278       {
2279         GetErrorDictionary().LogError(error, true);
2280         throw OrthancException(static_cast<ErrorCode>(error));
2281       }
2282     }
2283   }
2284 
2285 
2286 
SignalChange(const ServerIndexChange & change)2287   void OrthancPlugins::SignalChange(const ServerIndexChange& change)
2288   {
2289     SignalChangeInternal(Plugins::Convert(change.GetChangeType()),
2290                          Plugins::Convert(change.GetResourceType()),
2291                          change.GetPublicId().c_str());
2292   }
2293 
2294 
2295 
RegisterRestCallback(const void * parameters,bool mutualExclusion)2296   void OrthancPlugins::RegisterRestCallback(const void* parameters,
2297                                             bool mutualExclusion)
2298   {
2299     const _OrthancPluginRestCallback& p =
2300       *reinterpret_cast<const _OrthancPluginRestCallback*>(parameters);
2301 
2302     CLOG(INFO, PLUGINS) << "Plugin has registered a REST callback "
2303                         << (mutualExclusion ? "with" : "without")
2304                         << " mutual exclusion on: "
2305                         << p.pathRegularExpression;
2306 
2307     {
2308       boost::unique_lock<boost::shared_mutex> lock(pimpl_->restCallbackRegistrationMutex_);
2309       pimpl_->restCallbacks_.push_back(new PImpl::RestCallback(p.pathRegularExpression, p.callback, mutualExclusion));
2310     }
2311   }
2312 
2313 
RegisterChunkedRestCallback(const void * parameters)2314   void OrthancPlugins::RegisterChunkedRestCallback(const void* parameters)
2315   {
2316     const _OrthancPluginChunkedRestCallback& p =
2317       *reinterpret_cast<const _OrthancPluginChunkedRestCallback*>(parameters);
2318 
2319     CLOG(INFO, PLUGINS) << "Plugin has registered a REST callback for chunked streams on: "
2320                         << p.pathRegularExpression;
2321 
2322     {
2323       boost::unique_lock<boost::shared_mutex> lock(pimpl_->restCallbackRegistrationMutex_);
2324       pimpl_->chunkedRestCallbacks_.push_back(new PImpl::ChunkedRestCallback(p));
2325     }
2326   }
2327 
2328 
RegisterOnStoredInstanceCallback(const void * parameters)2329   void OrthancPlugins::RegisterOnStoredInstanceCallback(const void* parameters)
2330   {
2331     const _OrthancPluginOnStoredInstanceCallback& p =
2332       *reinterpret_cast<const _OrthancPluginOnStoredInstanceCallback*>(parameters);
2333 
2334     CLOG(INFO, PLUGINS) << "Plugin has registered an OnStoredInstance callback";
2335     pimpl_->onStoredCallbacks_.push_back(p.callback);
2336   }
2337 
2338 
RegisterOnChangeCallback(const void * parameters)2339   void OrthancPlugins::RegisterOnChangeCallback(const void* parameters)
2340   {
2341     const _OrthancPluginOnChangeCallback& p =
2342       *reinterpret_cast<const _OrthancPluginOnChangeCallback*>(parameters);
2343 
2344     CLOG(INFO, PLUGINS) << "Plugin has registered an OnChange callback";
2345     pimpl_->onChangeCallbacks_.push_back(p.callback);
2346   }
2347 
2348 
RegisterWorklistCallback(const void * parameters)2349   void OrthancPlugins::RegisterWorklistCallback(const void* parameters)
2350   {
2351     const _OrthancPluginWorklistCallback& p =
2352       *reinterpret_cast<const _OrthancPluginWorklistCallback*>(parameters);
2353 
2354     boost::mutex::scoped_lock lock(pimpl_->worklistCallbackMutex_);
2355 
2356     if (pimpl_->worklistCallback_ != NULL)
2357     {
2358       throw OrthancException(ErrorCode_Plugin,
2359                              "Can only register one plugin to handle modality worklists");
2360     }
2361     else
2362     {
2363       CLOG(INFO, PLUGINS) << "Plugin has registered a callback to handle modality worklists";
2364       pimpl_->worklistCallback_ = p.callback;
2365     }
2366   }
2367 
2368 
RegisterFindCallback(const void * parameters)2369   void OrthancPlugins::RegisterFindCallback(const void* parameters)
2370   {
2371     const _OrthancPluginFindCallback& p =
2372       *reinterpret_cast<const _OrthancPluginFindCallback*>(parameters);
2373 
2374     boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_);
2375 
2376     if (pimpl_->findCallback_ != NULL)
2377     {
2378       throw OrthancException(ErrorCode_Plugin,
2379                              "Can only register one plugin to handle C-FIND requests");
2380     }
2381     else
2382     {
2383       CLOG(INFO, PLUGINS) << "Plugin has registered a callback to handle C-FIND requests";
2384       pimpl_->findCallback_ = p.callback;
2385     }
2386   }
2387 
2388 
RegisterMoveCallback(const void * parameters)2389   void OrthancPlugins::RegisterMoveCallback(const void* parameters)
2390   {
2391     // invokeServiceMutex_ is assumed to be locked
2392 
2393     const _OrthancPluginMoveCallback& p =
2394       *reinterpret_cast<const _OrthancPluginMoveCallback*>(parameters);
2395 
2396     if (pimpl_->moveCallbacks_.callback != NULL)
2397     {
2398       throw OrthancException(ErrorCode_Plugin,
2399                              "Can only register one plugin to handle C-MOVE requests");
2400     }
2401     else
2402     {
2403       CLOG(INFO, PLUGINS) << "Plugin has registered a callback to handle C-MOVE requests";
2404       pimpl_->moveCallbacks_ = p;
2405     }
2406   }
2407 
2408 
RegisterDecodeImageCallback(const void * parameters)2409   void OrthancPlugins::RegisterDecodeImageCallback(const void* parameters)
2410   {
2411     const _OrthancPluginDecodeImageCallback& p =
2412       *reinterpret_cast<const _OrthancPluginDecodeImageCallback*>(parameters);
2413 
2414     boost::unique_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
2415 
2416     pimpl_->decodeImageCallbacks_.push_back(p.callback);
2417     CLOG(INFO, PLUGINS) << "Plugin has registered a callback to decode DICOM images ("
2418                         << pimpl_->decodeImageCallbacks_.size() << " decoder(s) now active)";
2419   }
2420 
2421 
RegisterTranscoderCallback(const void * parameters)2422   void OrthancPlugins::RegisterTranscoderCallback(const void* parameters)
2423   {
2424     const _OrthancPluginTranscoderCallback& p =
2425       *reinterpret_cast<const _OrthancPluginTranscoderCallback*>(parameters);
2426 
2427     boost::unique_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
2428 
2429     pimpl_->transcoderCallbacks_.push_back(p.callback);
2430     CLOG(INFO, PLUGINS) << "Plugin has registered a callback to transcode DICOM images ("
2431                         << pimpl_->transcoderCallbacks_.size() << " transcoder(s) now active)";
2432   }
2433 
2434 
RegisterJobsUnserializer(const void * parameters)2435   void OrthancPlugins::RegisterJobsUnserializer(const void* parameters)
2436   {
2437     const _OrthancPluginJobsUnserializer& p =
2438       *reinterpret_cast<const _OrthancPluginJobsUnserializer*>(parameters);
2439 
2440     boost::mutex::scoped_lock lock(pimpl_->jobsUnserializersMutex_);
2441 
2442     pimpl_->jobsUnserializers_.push_back(p.unserializer);
2443     CLOG(INFO, PLUGINS) << "Plugin has registered a callback to unserialize jobs ("
2444                         << pimpl_->jobsUnserializers_.size() << " unserializer(s) now active)";
2445   }
2446 
2447 
RegisterIncomingHttpRequestFilter(const void * parameters)2448   void OrthancPlugins::RegisterIncomingHttpRequestFilter(const void* parameters)
2449   {
2450     const _OrthancPluginIncomingHttpRequestFilter& p =
2451       *reinterpret_cast<const _OrthancPluginIncomingHttpRequestFilter*>(parameters);
2452 
2453     boost::unique_lock<boost::shared_mutex> lock(pimpl_->incomingHttpRequestFilterMutex_);
2454 
2455     CLOG(INFO, PLUGINS) << "Plugin has registered a callback to filter incoming HTTP requests";
2456     pimpl_->incomingHttpRequestFilters_.push_back(p.callback);
2457   }
2458 
2459 
RegisterIncomingHttpRequestFilter2(const void * parameters)2460   void OrthancPlugins::RegisterIncomingHttpRequestFilter2(const void* parameters)
2461   {
2462     const _OrthancPluginIncomingHttpRequestFilter2& p =
2463       *reinterpret_cast<const _OrthancPluginIncomingHttpRequestFilter2*>(parameters);
2464 
2465     boost::unique_lock<boost::shared_mutex> lock(pimpl_->incomingHttpRequestFilterMutex_);
2466 
2467     CLOG(INFO, PLUGINS) << "Plugin has registered a callback to filter incoming HTTP requests";
2468     pimpl_->incomingHttpRequestFilters2_.push_back(p.callback);
2469   }
2470 
2471 
RegisterIncomingDicomInstanceFilter(const void * parameters)2472   void OrthancPlugins::RegisterIncomingDicomInstanceFilter(const void* parameters)
2473   {
2474     const _OrthancPluginIncomingDicomInstanceFilter& p =
2475       *reinterpret_cast<const _OrthancPluginIncomingDicomInstanceFilter*>(parameters);
2476 
2477     CLOG(INFO, PLUGINS) << "Plugin has registered a callback to filter incoming DICOM instances";
2478     pimpl_->incomingDicomInstanceFilters_.push_back(p.callback);
2479   }
2480 
2481 
RegisterRefreshMetricsCallback(const void * parameters)2482   void OrthancPlugins::RegisterRefreshMetricsCallback(const void* parameters)
2483   {
2484     const _OrthancPluginRegisterRefreshMetricsCallback& p =
2485       *reinterpret_cast<const _OrthancPluginRegisterRefreshMetricsCallback*>(parameters);
2486 
2487     boost::mutex::scoped_lock lock(pimpl_->refreshMetricsMutex_);
2488 
2489     CLOG(INFO, PLUGINS) << "Plugin has registered a callback to refresh its metrics";
2490     pimpl_->refreshMetricsCallbacks_.push_back(p.callback);
2491   }
2492 
2493 
RegisterStorageCommitmentScpCallback(const void * parameters)2494   void OrthancPlugins::RegisterStorageCommitmentScpCallback(const void* parameters)
2495   {
2496     const _OrthancPluginRegisterStorageCommitmentScpCallback& p =
2497       *reinterpret_cast<const _OrthancPluginRegisterStorageCommitmentScpCallback*>(parameters);
2498 
2499     boost::mutex::scoped_lock lock(pimpl_->storageCommitmentScpMutex_);
2500     CLOG(INFO, PLUGINS) << "Plugin has registered a storage commitment callback";
2501 
2502     pimpl_->storageCommitmentScpCallbacks_.push_back(new PImpl::StorageCommitmentScp(p));
2503   }
2504 
2505 
AnswerBuffer(const void * parameters)2506   void OrthancPlugins::AnswerBuffer(const void* parameters)
2507   {
2508     const _OrthancPluginAnswerBuffer& p =
2509       *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters);
2510 
2511     HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2512     translatedOutput.SetContentType(p.mimeType);
2513     translatedOutput.Answer(p.answer, p.answerSize);
2514   }
2515 
2516 
Redirect(const void * parameters)2517   void OrthancPlugins::Redirect(const void* parameters)
2518   {
2519     const _OrthancPluginOutputPlusArgument& p =
2520       *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters);
2521 
2522     HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2523     translatedOutput.Redirect(p.argument);
2524   }
2525 
2526 
SendHttpStatusCode(const void * parameters)2527   void OrthancPlugins::SendHttpStatusCode(const void* parameters)
2528   {
2529     const _OrthancPluginSendHttpStatusCode& p =
2530       *reinterpret_cast<const _OrthancPluginSendHttpStatusCode*>(parameters);
2531 
2532     HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2533     translatedOutput.SendStatus(static_cast<HttpStatus>(p.status));
2534   }
2535 
2536 
SendHttpStatus(const void * parameters)2537   void OrthancPlugins::SendHttpStatus(const void* parameters)
2538   {
2539     const _OrthancPluginSendHttpStatus& p =
2540       *reinterpret_cast<const _OrthancPluginSendHttpStatus*>(parameters);
2541 
2542     HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2543     HttpStatus status = static_cast<HttpStatus>(p.status);
2544 
2545     if (p.bodySize > 0 && p.body != NULL)
2546     {
2547       translatedOutput.SendStatus(status, p.body, p.bodySize);
2548     }
2549     else
2550     {
2551       translatedOutput.SendStatus(status);
2552     }
2553   }
2554 
2555 
SendUnauthorized(const void * parameters)2556   void OrthancPlugins::SendUnauthorized(const void* parameters)
2557   {
2558     const _OrthancPluginOutputPlusArgument& p =
2559       *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters);
2560 
2561     HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2562     translatedOutput.SendUnauthorized(p.argument);
2563   }
2564 
2565 
SendMethodNotAllowed(const void * parameters)2566   void OrthancPlugins::SendMethodNotAllowed(const void* parameters)
2567   {
2568     const _OrthancPluginOutputPlusArgument& p =
2569       *reinterpret_cast<const _OrthancPluginOutputPlusArgument*>(parameters);
2570 
2571     HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2572     translatedOutput.SendMethodNotAllowed(p.argument);
2573   }
2574 
2575 
SetCookie(const void * parameters)2576   void OrthancPlugins::SetCookie(const void* parameters)
2577   {
2578     const _OrthancPluginSetHttpHeader& p =
2579       *reinterpret_cast<const _OrthancPluginSetHttpHeader*>(parameters);
2580 
2581     HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2582     translatedOutput.SetCookie(p.key, p.value);
2583   }
2584 
2585 
SetHttpHeader(const void * parameters)2586   void OrthancPlugins::SetHttpHeader(const void* parameters)
2587   {
2588     const _OrthancPluginSetHttpHeader& p =
2589       *reinterpret_cast<const _OrthancPluginSetHttpHeader*>(parameters);
2590 
2591     HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2592     translatedOutput.AddHeader(p.key, p.value);
2593   }
2594 
2595 
SetHttpErrorDetails(const void * parameters)2596   void OrthancPlugins::SetHttpErrorDetails(const void* parameters)
2597   {
2598     const _OrthancPluginSetHttpErrorDetails& p =
2599       *reinterpret_cast<const _OrthancPluginSetHttpErrorDetails*>(parameters);
2600 
2601     PImpl::PluginHttpOutput* output =
2602       reinterpret_cast<PImpl::PluginHttpOutput*>(p.output);
2603     output->SetErrorDetails(p.details, (p.log != 0));
2604   }
2605 
2606 
CompressAndAnswerPngImage(const void * parameters)2607   void OrthancPlugins::CompressAndAnswerPngImage(const void* parameters)
2608   {
2609     // Bridge for backward compatibility with Orthanc <= 0.9.3
2610     const _OrthancPluginCompressAndAnswerPngImage& p =
2611       *reinterpret_cast<const _OrthancPluginCompressAndAnswerPngImage*>(parameters);
2612 
2613     _OrthancPluginCompressAndAnswerImage p2;
2614     p2.output = p.output;
2615     p2.imageFormat = OrthancPluginImageFormat_Png;
2616     p2.pixelFormat = p.format;
2617     p2.width = p.width;
2618     p2.height = p.height;
2619     p2.pitch = p.height;
2620     p2.buffer = p.buffer;
2621     p2.quality = 0;
2622 
2623     CompressAndAnswerImage(&p2);
2624   }
2625 
2626 
CompressAndAnswerImage(const void * parameters)2627   void OrthancPlugins::CompressAndAnswerImage(const void* parameters)
2628   {
2629     const _OrthancPluginCompressAndAnswerImage& p =
2630       *reinterpret_cast<const _OrthancPluginCompressAndAnswerImage*>(parameters);
2631 
2632     HttpOutput& translatedOutput = reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->GetOutput();
2633 
2634     ImageAccessor accessor;
2635     accessor.AssignReadOnly(Plugins::Convert(p.pixelFormat), p.width, p.height, p.pitch, p.buffer);
2636 
2637     std::string compressed;
2638 
2639     switch (p.imageFormat)
2640     {
2641       case OrthancPluginImageFormat_Png:
2642       {
2643         PngWriter writer;
2644         IImageWriter::WriteToMemory(writer, compressed, accessor);
2645         translatedOutput.SetContentType(MimeType_Png);
2646         break;
2647       }
2648 
2649       case OrthancPluginImageFormat_Jpeg:
2650       {
2651         JpegWriter writer;
2652         writer.SetQuality(p.quality);
2653         IImageWriter::WriteToMemory(writer, compressed, accessor);
2654         translatedOutput.SetContentType(MimeType_Jpeg);
2655         break;
2656       }
2657 
2658       default:
2659         throw OrthancException(ErrorCode_ParameterOutOfRange);
2660     }
2661 
2662     translatedOutput.Answer(compressed);
2663   }
2664 
2665 
GetDicomForInstance(const void * parameters)2666   void OrthancPlugins::GetDicomForInstance(const void* parameters)
2667   {
2668     const _OrthancPluginGetDicomForInstance& p =
2669       *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters);
2670 
2671     std::string dicom;
2672 
2673     {
2674       PImpl::ServerContextLock lock(*pimpl_);
2675       lock.GetContext().ReadDicom(dicom, p.instanceId);
2676     }
2677 
2678     CopyToMemoryBuffer(*p.target, dicom);
2679   }
2680 
2681 
RestApiGet(const void * parameters,bool afterPlugins)2682   void OrthancPlugins::RestApiGet(const void* parameters,
2683                                   bool afterPlugins)
2684   {
2685     const _OrthancPluginRestApiGet& p =
2686       *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters);
2687 
2688     CLOG(INFO, PLUGINS) << "Plugin making REST GET call on URI " << p.uri
2689                         << (afterPlugins ? " (after plugins)" : " (built-in API)");
2690 
2691     IHttpHandler* handler;
2692 
2693     {
2694       PImpl::ServerContextLock lock(*pimpl_);
2695       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
2696     }
2697 
2698     std::map<std::string, std::string> httpHeaders;
2699 
2700     std::string result;
2701     if (IHttpHandler::SimpleGet(result, NULL, *handler, RequestOrigin_Plugins, p.uri, httpHeaders) == HttpStatus_200_Ok)
2702     {
2703       CopyToMemoryBuffer(*p.target, result);
2704     }
2705     else
2706     {
2707       throw OrthancException(ErrorCode_UnknownResource);
2708     }
2709   }
2710 
2711 
RestApiGet2(const void * parameters)2712   void OrthancPlugins::RestApiGet2(const void* parameters)
2713   {
2714     const _OrthancPluginRestApiGet2& p =
2715       *reinterpret_cast<const _OrthancPluginRestApiGet2*>(parameters);
2716 
2717     CLOG(INFO, PLUGINS) << "Plugin making REST GET call on URI " << p.uri
2718                         << (p.afterPlugins ? " (after plugins)" : " (built-in API)");
2719 
2720     HttpToolbox::Arguments headers;
2721 
2722     for (uint32_t i = 0; i < p.headersCount; i++)
2723     {
2724       std::string name(p.headersKeys[i]);
2725       std::transform(name.begin(), name.end(), name.begin(), ::tolower);
2726       headers[name] = p.headersValues[i];
2727     }
2728 
2729     IHttpHandler* handler;
2730 
2731     {
2732       PImpl::ServerContextLock lock(*pimpl_);
2733       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins);
2734     }
2735 
2736     std::string result;
2737     if (IHttpHandler::SimpleGet(result, NULL, *handler, RequestOrigin_Plugins, p.uri, headers) == HttpStatus_200_Ok)
2738     {
2739       CopyToMemoryBuffer(*p.target, result);
2740     }
2741     else
2742     {
2743       throw OrthancException(ErrorCode_UnknownResource);
2744     }
2745   }
2746 
2747 
RestApiPostPut(bool isPost,const void * parameters,bool afterPlugins)2748   void OrthancPlugins::RestApiPostPut(bool isPost,
2749                                       const void* parameters,
2750                                       bool afterPlugins)
2751   {
2752     const _OrthancPluginRestApiPostPut& p =
2753       *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters);
2754 
2755     CLOG(INFO, PLUGINS) << "Plugin making REST " << EnumerationToString(isPost ? HttpMethod_Post : HttpMethod_Put)
2756                         << " call on URI " << p.uri << (afterPlugins ? " (after plugins)" : " (built-in API)");
2757 
2758     IHttpHandler* handler;
2759 
2760     {
2761       PImpl::ServerContextLock lock(*pimpl_);
2762       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
2763     }
2764 
2765     std::map<std::string, std::string> httpHeaders;
2766 
2767     std::string result;
2768     if (isPost ?
2769         IHttpHandler::SimplePost(result, NULL, *handler, RequestOrigin_Plugins, p.uri,
2770                                  p.body, p.bodySize, httpHeaders) == HttpStatus_200_Ok :
2771         IHttpHandler::SimplePut(result, NULL, *handler, RequestOrigin_Plugins, p.uri,
2772                                 p.body, p.bodySize, httpHeaders) == HttpStatus_200_Ok)
2773     {
2774       CopyToMemoryBuffer(*p.target, result);
2775     }
2776     else
2777     {
2778       throw OrthancException(ErrorCode_UnknownResource);
2779     }
2780   }
2781 
2782 
RestApiDelete(const void * parameters,bool afterPlugins)2783   void OrthancPlugins::RestApiDelete(const void* parameters,
2784                                      bool afterPlugins)
2785   {
2786     const char* uri = reinterpret_cast<const char*>(parameters);
2787     CLOG(INFO, PLUGINS) << "Plugin making REST DELETE call on URI " << uri
2788                         << (afterPlugins ? " (after plugins)" : " (built-in API)");
2789 
2790     IHttpHandler* handler;
2791 
2792     {
2793       PImpl::ServerContextLock lock(*pimpl_);
2794       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!afterPlugins);
2795     }
2796 
2797     std::map<std::string, std::string> httpHeaders;
2798 
2799     if (IHttpHandler::SimpleDelete(NULL, *handler, RequestOrigin_Plugins, uri, httpHeaders) != HttpStatus_200_Ok)
2800     {
2801       throw OrthancException(ErrorCode_UnknownResource);
2802     }
2803   }
2804 
2805 
LookupResource(_OrthancPluginService service,const void * parameters)2806   void OrthancPlugins::LookupResource(_OrthancPluginService service,
2807                                       const void* parameters)
2808   {
2809     const _OrthancPluginRetrieveDynamicString& p =
2810       *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters);
2811 
2812     /**
2813      * The enumeration below only uses the tags that are indexed in
2814      * the Orthanc database. It reflects the
2815      * "CandidateResources::ApplyFilter()" method of the
2816      * "OrthancFindRequestHandler" class.
2817      **/
2818 
2819     DicomTag tag(0, 0);
2820     ResourceType level;
2821     switch (service)
2822     {
2823       case _OrthancPluginService_LookupPatient:
2824         tag = DICOM_TAG_PATIENT_ID;
2825         level = ResourceType_Patient;
2826         break;
2827 
2828       case _OrthancPluginService_LookupStudy:
2829         tag = DICOM_TAG_STUDY_INSTANCE_UID;
2830         level = ResourceType_Study;
2831         break;
2832 
2833       case _OrthancPluginService_LookupStudyWithAccessionNumber:
2834         tag = DICOM_TAG_ACCESSION_NUMBER;
2835         level = ResourceType_Study;
2836         break;
2837 
2838       case _OrthancPluginService_LookupSeries:
2839         tag = DICOM_TAG_SERIES_INSTANCE_UID;
2840         level = ResourceType_Series;
2841         break;
2842 
2843       case _OrthancPluginService_LookupInstance:
2844         tag = DICOM_TAG_SOP_INSTANCE_UID;
2845         level = ResourceType_Instance;
2846         break;
2847 
2848       default:
2849         throw OrthancException(ErrorCode_InternalError);
2850     }
2851 
2852     std::vector<std::string> result;
2853 
2854     {
2855       PImpl::ServerContextLock lock(*pimpl_);
2856       lock.GetContext().GetIndex().LookupIdentifierExact(result, level, tag, p.argument);
2857     }
2858 
2859     if (result.size() == 1)
2860     {
2861       *p.result = CopyString(result[0]);
2862     }
2863     else
2864     {
2865       if (result.size() > 1)
2866       {
2867         LOG(WARNING) << "LookupResource(): Multiple resources match the query (instead of 0 or 1), which indicates "
2868                      << "your DICOM database breaks the DICOM model of the real world";
2869       }
2870 
2871       throw OrthancException(ErrorCode_UnknownResource);
2872     }
2873   }
2874 
2875 
AccessInstanceMetadataInternal(bool checkExistence,const _OrthancPluginAccessDicomInstance & params,const DicomInstanceToStore & instance)2876   static void AccessInstanceMetadataInternal(bool checkExistence,
2877                                              const _OrthancPluginAccessDicomInstance& params,
2878                                              const DicomInstanceToStore& instance)
2879   {
2880     MetadataType metadata;
2881 
2882     try
2883     {
2884       metadata = StringToMetadata(params.key);
2885     }
2886     catch (OrthancException&)
2887     {
2888       // Unknown metadata
2889       if (checkExistence)
2890       {
2891         *params.resultInt64 = -1;
2892       }
2893       else
2894       {
2895         *params.resultString = NULL;
2896       }
2897 
2898       return;
2899     }
2900 
2901     ServerIndex::MetadataMap::const_iterator it =
2902       instance.GetMetadata().find(std::make_pair(ResourceType_Instance, metadata));
2903 
2904     if (checkExistence)
2905     {
2906       if (it != instance.GetMetadata().end())
2907       {
2908         *params.resultInt64 = 1;
2909       }
2910       else
2911       {
2912         *params.resultInt64 = 0;
2913       }
2914     }
2915     else
2916     {
2917       if (it != instance.GetMetadata().end())
2918       {
2919         *params.resultString = it->second.c_str();
2920       }
2921       else
2922       {
2923         // Error: Missing metadata
2924         *params.resultString = NULL;
2925       }
2926     }
2927   }
2928 
2929 
AccessDicomInstance(_OrthancPluginService service,const void * parameters)2930   void OrthancPlugins::AccessDicomInstance(_OrthancPluginService service,
2931                                            const void* parameters)
2932   {
2933     const _OrthancPluginAccessDicomInstance& p =
2934       *reinterpret_cast<const _OrthancPluginAccessDicomInstance*>(parameters);
2935 
2936     if (p.instance == NULL)
2937     {
2938       throw OrthancException(ErrorCode_NullPointer);
2939     }
2940 
2941     const DicomInstanceToStore& instance =
2942       reinterpret_cast<const IDicomInstance*>(p.instance)->GetInstance();
2943 
2944     switch (service)
2945     {
2946       case _OrthancPluginService_GetInstanceRemoteAet:
2947         *p.resultString = instance.GetOrigin().GetRemoteAetC();
2948         return;
2949 
2950       case _OrthancPluginService_GetInstanceSize:
2951         *p.resultInt64 = instance.GetBufferSize();
2952         return;
2953 
2954       case _OrthancPluginService_GetInstanceData:
2955         *p.resultString = reinterpret_cast<const char*>(instance.GetBufferData());
2956         return;
2957 
2958       case _OrthancPluginService_HasInstanceMetadata:
2959         AccessInstanceMetadataInternal(true, p, instance);
2960         return;
2961 
2962       case _OrthancPluginService_GetInstanceMetadata:
2963         AccessInstanceMetadataInternal(false, p, instance);
2964         return;
2965 
2966       case _OrthancPluginService_GetInstanceJson:
2967       case _OrthancPluginService_GetInstanceSimplifiedJson:
2968       {
2969         Json::Value dicomAsJson;
2970         instance.GetDicomAsJson(dicomAsJson);
2971 
2972         std::string s;
2973         if (service == _OrthancPluginService_GetInstanceJson)
2974         {
2975           Toolbox::WriteStyledJson(s, dicomAsJson);
2976         }
2977         else
2978         {
2979           Json::Value simplified;
2980           Toolbox::SimplifyDicomAsJson(simplified, dicomAsJson, DicomToJsonFormat_Human);
2981           Toolbox::WriteStyledJson(s, simplified);
2982         }
2983 
2984         *p.resultStringToFree = CopyString(s);
2985         return;
2986       }
2987 
2988       case _OrthancPluginService_GetInstanceOrigin:   // New in Orthanc 0.9.5
2989         *p.resultOrigin = Plugins::Convert(instance.GetOrigin().GetRequestOrigin());
2990         return;
2991 
2992       case _OrthancPluginService_GetInstanceTransferSyntaxUid:   // New in Orthanc 1.6.1
2993       {
2994         DicomTransferSyntax s;
2995         if (instance.LookupTransferSyntax(s))
2996         {
2997           *p.resultStringToFree = CopyString(GetTransferSyntaxUid(s));
2998         }
2999         else
3000         {
3001           *p.resultStringToFree = CopyString("");
3002         }
3003 
3004         return;
3005       }
3006 
3007       case _OrthancPluginService_HasInstancePixelData:   // New in Orthanc 1.6.1
3008         *p.resultInt64 = instance.HasPixelData();
3009         return;
3010 
3011       case _OrthancPluginService_GetInstanceFramesCount:  // New in Orthanc 1.7.0
3012         *p.resultInt64 = instance.GetFramesCount();
3013         return;
3014 
3015       default:
3016         throw OrthancException(ErrorCode_InternalError);
3017     }
3018   }
3019 
3020 
BufferCompression(const void * parameters)3021   void OrthancPlugins::BufferCompression(const void* parameters)
3022   {
3023     const _OrthancPluginBufferCompression& p =
3024       *reinterpret_cast<const _OrthancPluginBufferCompression*>(parameters);
3025 
3026     std::string result;
3027 
3028     {
3029       std::unique_ptr<DeflateBaseCompressor> compressor;
3030 
3031       switch (p.compression)
3032       {
3033         case OrthancPluginCompressionType_Zlib:
3034         {
3035           compressor.reset(new ZlibCompressor);
3036           compressor->SetPrefixWithUncompressedSize(false);
3037           break;
3038         }
3039 
3040         case OrthancPluginCompressionType_ZlibWithSize:
3041         {
3042           compressor.reset(new ZlibCompressor);
3043           compressor->SetPrefixWithUncompressedSize(true);
3044           break;
3045         }
3046 
3047         case OrthancPluginCompressionType_Gzip:
3048         {
3049           compressor.reset(new GzipCompressor);
3050           compressor->SetPrefixWithUncompressedSize(false);
3051           break;
3052         }
3053 
3054         case OrthancPluginCompressionType_GzipWithSize:
3055         {
3056           compressor.reset(new GzipCompressor);
3057           compressor->SetPrefixWithUncompressedSize(true);
3058           break;
3059         }
3060 
3061         default:
3062           throw OrthancException(ErrorCode_ParameterOutOfRange);
3063       }
3064 
3065       if (p.uncompress)
3066       {
3067         compressor->Uncompress(result, p.source, p.size);
3068       }
3069       else
3070       {
3071         compressor->Compress(result, p.source, p.size);
3072       }
3073     }
3074 
3075     CopyToMemoryBuffer(*p.target, result);
3076   }
3077 
3078 
ReturnImage(std::unique_ptr<ImageAccessor> & image)3079   static OrthancPluginImage* ReturnImage(std::unique_ptr<ImageAccessor>& image)
3080   {
3081     // Images returned to plugins are assumed to be writeable. If the
3082     // input image is read-only, we return a copy so that it can be modified.
3083 
3084     if (image.get() == NULL)
3085     {
3086       throw OrthancException(ErrorCode_NullPointer);
3087     }
3088 
3089     if (image->IsReadOnly())
3090     {
3091       std::unique_ptr<Image> copy(new Image(image->GetFormat(), image->GetWidth(), image->GetHeight(), false));
3092       ImageProcessing::Copy(*copy, *image);
3093       image.reset(NULL);
3094       return reinterpret_cast<OrthancPluginImage*>(copy.release());
3095     }
3096     else
3097     {
3098       return reinterpret_cast<OrthancPluginImage*>(image.release());
3099     }
3100   }
3101 
3102 
AccessDicomInstance2(_OrthancPluginService service,const void * parameters)3103   void OrthancPlugins::AccessDicomInstance2(_OrthancPluginService service,
3104                                             const void* parameters)
3105   {
3106     const _OrthancPluginAccessDicomInstance2& p =
3107       *reinterpret_cast<const _OrthancPluginAccessDicomInstance2*>(parameters);
3108 
3109     if (p.instance == NULL)
3110     {
3111       throw OrthancException(ErrorCode_NullPointer);
3112     }
3113 
3114     const DicomInstanceToStore& instance =
3115       reinterpret_cast<const IDicomInstance*>(p.instance)->GetInstance();
3116 
3117     switch (service)
3118     {
3119       case _OrthancPluginService_GetInstanceFramesCount:
3120         *p.targetUint32 = instance.GetFramesCount();
3121         return;
3122 
3123       case _OrthancPluginService_GetInstanceRawFrame:
3124       {
3125         if (p.targetBuffer == NULL)
3126         {
3127           throw OrthancException(ErrorCode_NullPointer);
3128         }
3129 
3130         p.targetBuffer->data = NULL;
3131         p.targetBuffer->size = 0;
3132 
3133         MimeType mime;
3134         std::string frame;
3135         instance.GetParsedDicomFile().GetRawFrame(frame, mime, p.frameIndex);
3136         CopyToMemoryBuffer(*p.targetBuffer, frame);
3137         return;
3138       }
3139 
3140       case _OrthancPluginService_GetInstanceDecodedFrame:
3141       {
3142         if (p.targetImage == NULL)
3143         {
3144           throw OrthancException(ErrorCode_NullPointer);
3145         }
3146 
3147         std::unique_ptr<ImageAccessor> decoded;
3148         {
3149           PImpl::ServerContextLock lock(*pimpl_);
3150           decoded.reset(lock.GetContext().DecodeDicomFrame(instance, p.frameIndex));
3151         }
3152 
3153         *(p.targetImage) = ReturnImage(decoded);
3154         return;
3155       }
3156 
3157       case _OrthancPluginService_SerializeDicomInstance:
3158       {
3159         if (p.targetBuffer == NULL)
3160         {
3161           throw OrthancException(ErrorCode_NullPointer);
3162         }
3163 
3164         p.targetBuffer->data = NULL;
3165         p.targetBuffer->size = 0;
3166         CopyToMemoryBuffer(*p.targetBuffer, instance.GetBufferData(), instance.GetBufferSize());
3167         return;
3168       }
3169 
3170       case _OrthancPluginService_GetInstanceAdvancedJson:
3171       {
3172         if (p.targetStringToFree == NULL)
3173         {
3174           throw OrthancException(ErrorCode_NullPointer);
3175         }
3176 
3177         Json::Value json;
3178         instance.DatasetToJson(json, Plugins::Convert(p.format),
3179                                static_cast<DicomToJsonFlags>(p.flags), p.maxStringLength);
3180 
3181         std::string s;
3182         Toolbox::WriteFastJson(s, json);
3183         *p.targetStringToFree = CopyString(s);
3184         return;
3185       }
3186 
3187       case _OrthancPluginService_GetInstanceDicomWebJson:
3188       case _OrthancPluginService_GetInstanceDicomWebXml:
3189       {
3190         if (p.targetStringToFree == NULL)
3191         {
3192           throw OrthancException(ErrorCode_NullPointer);
3193         }
3194 
3195         DicomWebBinaryFormatter formatter(p.dicomWebCallback, p.dicomWebPayload);
3196         formatter.Apply(p.targetStringToFree,
3197                         (service == _OrthancPluginService_GetInstanceDicomWebJson),
3198                         instance.GetParsedDicomFile());
3199         return;
3200       }
3201 
3202       default:
3203         throw OrthancException(ErrorCode_InternalError);
3204     }
3205   }
3206 
3207 
UncompressImage(const void * parameters)3208   void OrthancPlugins::UncompressImage(const void* parameters)
3209   {
3210     const _OrthancPluginUncompressImage& p = *reinterpret_cast<const _OrthancPluginUncompressImage*>(parameters);
3211 
3212     std::unique_ptr<ImageAccessor> image;
3213 
3214     switch (p.format)
3215     {
3216       case OrthancPluginImageFormat_Png:
3217       {
3218         image.reset(new PngReader);
3219         reinterpret_cast<PngReader&>(*image).ReadFromMemory(p.data, p.size);
3220         break;
3221       }
3222 
3223       case OrthancPluginImageFormat_Jpeg:
3224       {
3225         image.reset(new JpegReader);
3226         reinterpret_cast<JpegReader&>(*image).ReadFromMemory(p.data, p.size);
3227         break;
3228       }
3229 
3230       case OrthancPluginImageFormat_Dicom:
3231       {
3232         PImpl::ServerContextLock lock(*pimpl_);
3233         image.reset(lock.GetContext().DecodeDicomFrame(p.data, p.size, 0));
3234         break;
3235       }
3236 
3237       default:
3238         throw OrthancException(ErrorCode_ParameterOutOfRange);
3239     }
3240 
3241     *(p.target) = ReturnImage(image);
3242   }
3243 
3244 
CompressImage(const void * parameters)3245   void OrthancPlugins::CompressImage(const void* parameters)
3246   {
3247     const _OrthancPluginCompressImage& p = *reinterpret_cast<const _OrthancPluginCompressImage*>(parameters);
3248 
3249     std::string compressed;
3250 
3251     ImageAccessor accessor;
3252     accessor.AssignReadOnly(Plugins::Convert(p.pixelFormat), p.width, p.height, p.pitch, p.buffer);
3253 
3254     switch (p.imageFormat)
3255     {
3256       case OrthancPluginImageFormat_Png:
3257       {
3258         PngWriter writer;
3259         IImageWriter::WriteToMemory(writer, compressed, accessor);
3260         break;
3261       }
3262 
3263       case OrthancPluginImageFormat_Jpeg:
3264       {
3265         JpegWriter writer;
3266         writer.SetQuality(p.quality);
3267         IImageWriter::WriteToMemory(writer, compressed, accessor);
3268         break;
3269       }
3270 
3271       default:
3272         throw OrthancException(ErrorCode_ParameterOutOfRange);
3273     }
3274 
3275     CopyToMemoryBuffer(*p.target, compressed.size() > 0 ? compressed.c_str() : NULL, compressed.size());
3276   }
3277 
3278 
SetupHttpClient(HttpClient & client,const _OrthancPluginCallHttpClient2 & parameters)3279   static void SetupHttpClient(HttpClient& client,
3280                               const _OrthancPluginCallHttpClient2& parameters)
3281   {
3282     client.SetUrl(parameters.url);
3283     client.SetConvertHeadersToLowerCase(false);
3284 
3285     if (parameters.timeout != 0)
3286     {
3287       client.SetTimeout(parameters.timeout);
3288     }
3289 
3290     if (parameters.username != NULL &&
3291         parameters.password != NULL)
3292     {
3293       client.SetCredentials(parameters.username, parameters.password);
3294     }
3295 
3296     if (parameters.certificateFile != NULL)
3297     {
3298       std::string certificate(parameters.certificateFile);
3299       std::string key, password;
3300 
3301       if (parameters.certificateKeyFile)
3302       {
3303         key.assign(parameters.certificateKeyFile);
3304       }
3305 
3306       if (parameters.certificateKeyPassword)
3307       {
3308         password.assign(parameters.certificateKeyPassword);
3309       }
3310 
3311       client.SetClientCertificate(certificate, key, password);
3312     }
3313 
3314     client.SetPkcs11Enabled(parameters.pkcs11 ? true : false);
3315 
3316     for (uint32_t i = 0; i < parameters.headersCount; i++)
3317     {
3318       if (parameters.headersKeys[i] == NULL ||
3319           parameters.headersValues[i] == NULL)
3320       {
3321         throw OrthancException(ErrorCode_NullPointer);
3322       }
3323 
3324       client.AddHeader(parameters.headersKeys[i], parameters.headersValues[i]);
3325     }
3326 
3327     switch (parameters.method)
3328     {
3329       case OrthancPluginHttpMethod_Get:
3330         client.SetMethod(HttpMethod_Get);
3331         break;
3332 
3333       case OrthancPluginHttpMethod_Post:
3334         client.SetMethod(HttpMethod_Post);
3335         client.SetExternalBody(parameters.body, parameters.bodySize);
3336         break;
3337 
3338       case OrthancPluginHttpMethod_Put:
3339         client.SetMethod(HttpMethod_Put);
3340         client.SetExternalBody(parameters.body, parameters.bodySize);
3341         break;
3342 
3343       case OrthancPluginHttpMethod_Delete:
3344         client.SetMethod(HttpMethod_Delete);
3345         break;
3346 
3347       default:
3348         throw OrthancException(ErrorCode_ParameterOutOfRange);
3349     }
3350   }
3351 
3352 
ExecuteHttpClientWithoutChunkedBody(uint16_t & httpStatus,OrthancPluginMemoryBuffer * answerBody,OrthancPluginMemoryBuffer * answerHeaders,HttpClient & client)3353   static void ExecuteHttpClientWithoutChunkedBody(uint16_t& httpStatus,
3354                                                   OrthancPluginMemoryBuffer* answerBody,
3355                                                   OrthancPluginMemoryBuffer* answerHeaders,
3356                                                   HttpClient& client)
3357   {
3358     std::string body;
3359     HttpClient::HttpHeaders headers;
3360 
3361     bool success = client.Apply(body, headers);
3362 
3363     // The HTTP request has succeeded
3364     httpStatus = static_cast<uint16_t>(client.GetLastStatus());
3365 
3366     if (!success)
3367     {
3368       HttpClient::ThrowException(client.GetLastStatus());
3369     }
3370 
3371     // Copy the HTTP headers of the answer, if the plugin requested them
3372     if (answerHeaders != NULL)
3373     {
3374       CopyDictionary(*answerHeaders, headers);
3375     }
3376 
3377     // Copy the body of the answer if it makes sense
3378     if (client.GetMethod() != HttpMethod_Delete)
3379     {
3380       try
3381       {
3382         if (answerBody != NULL)
3383         {
3384           CopyToMemoryBuffer(*answerBody, body);
3385         }
3386       }
3387       catch (OrthancException&)
3388       {
3389         if (answerHeaders != NULL)
3390         {
3391           free(answerHeaders->data);
3392         }
3393         throw;
3394       }
3395     }
3396   }
3397 
3398 
CallHttpClient(const void * parameters)3399   void OrthancPlugins::CallHttpClient(const void* parameters)
3400   {
3401     const _OrthancPluginCallHttpClient& p = *reinterpret_cast<const _OrthancPluginCallHttpClient*>(parameters);
3402 
3403     HttpClient client;
3404 
3405     {
3406       _OrthancPluginCallHttpClient2 converted;
3407       memset(&converted, 0, sizeof(converted));
3408 
3409       converted.answerBody = NULL;
3410       converted.answerHeaders = NULL;
3411       converted.httpStatus = NULL;
3412       converted.method = p.method;
3413       converted.url = p.url;
3414       converted.headersCount = 0;
3415       converted.headersKeys = NULL;
3416       converted.headersValues = NULL;
3417       converted.body = p.body;
3418       converted.bodySize = p.bodySize;
3419       converted.username = p.username;
3420       converted.password = p.password;
3421       converted.timeout = 0;  // Use default timeout
3422       converted.certificateFile = NULL;
3423       converted.certificateKeyFile = NULL;
3424       converted.certificateKeyPassword = NULL;
3425       converted.pkcs11 = false;
3426 
3427       SetupHttpClient(client, converted);
3428     }
3429 
3430     uint16_t status;
3431     ExecuteHttpClientWithoutChunkedBody(status, p.target, NULL, client);
3432   }
3433 
3434 
CallHttpClient2(const void * parameters)3435   void OrthancPlugins::CallHttpClient2(const void* parameters)
3436   {
3437     const _OrthancPluginCallHttpClient2& p = *reinterpret_cast<const _OrthancPluginCallHttpClient2*>(parameters);
3438 
3439     if (p.httpStatus == NULL)
3440     {
3441       throw OrthancException(ErrorCode_NullPointer);
3442     }
3443 
3444     HttpClient client;
3445 
3446     if (p.method == OrthancPluginHttpMethod_Post ||
3447         p.method == OrthancPluginHttpMethod_Put)
3448     {
3449       client.SetExternalBody(p.body, p.bodySize);
3450     }
3451 
3452     SetupHttpClient(client, p);
3453     ExecuteHttpClientWithoutChunkedBody(*p.httpStatus, p.answerBody, p.answerHeaders, client);
3454   }
3455 
3456 
ChunkedHttpClient(const void * parameters)3457   void OrthancPlugins::ChunkedHttpClient(const void* parameters)
3458   {
3459     const _OrthancPluginChunkedHttpClient& p =
3460       *reinterpret_cast<const _OrthancPluginChunkedHttpClient*>(parameters);
3461 
3462     if (p.httpStatus == NULL)
3463     {
3464       throw OrthancException(ErrorCode_NullPointer);
3465     }
3466 
3467     HttpClient client;
3468 
3469     {
3470       _OrthancPluginCallHttpClient2 converted;
3471       memset(&converted, 0, sizeof(converted));
3472 
3473       converted.answerBody = NULL;
3474       converted.answerHeaders = NULL;
3475       converted.httpStatus = NULL;
3476       converted.method = p.method;
3477       converted.url = p.url;
3478       converted.headersCount = p.headersCount;
3479       converted.headersKeys = p.headersKeys;
3480       converted.headersValues = p.headersValues;
3481       converted.body = NULL;
3482       converted.bodySize = 0;
3483       converted.username = p.username;
3484       converted.password = p.password;
3485       converted.timeout = p.timeout;
3486       converted.certificateFile = p.certificateFile;
3487       converted.certificateKeyFile = p.certificateKeyFile;
3488       converted.certificateKeyPassword = p.certificateKeyPassword;
3489       converted.pkcs11 = p.pkcs11;
3490 
3491       SetupHttpClient(client, converted);
3492     }
3493 
3494     HttpClientChunkedRequest body(p, pimpl_->dictionary_);
3495     client.SetBody(body);
3496 
3497     HttpClientChunkedAnswer answer(p, pimpl_->dictionary_);
3498 
3499     bool success = client.Apply(answer);
3500 
3501     *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus());
3502 
3503     if (!success)
3504     {
3505       HttpClient::ThrowException(client.GetLastStatus());
3506     }
3507   }
3508 
3509 
CallRestApi(const void * parameters)3510   void OrthancPlugins::CallRestApi(const void* parameters)
3511   {
3512     const _OrthancPluginCallRestApi& p = *reinterpret_cast<const _OrthancPluginCallRestApi*>(parameters);
3513 
3514     if (p.httpStatus == NULL)
3515     {
3516       throw OrthancException(ErrorCode_NullPointer);
3517     }
3518 
3519     const char* methodString;
3520     switch (p.method)
3521     {
3522       case OrthancPluginHttpMethod_Get:
3523         methodString = "GET";
3524         break;
3525 
3526       case OrthancPluginHttpMethod_Post:
3527         methodString = "POST";
3528         break;
3529 
3530       case OrthancPluginHttpMethod_Put:
3531         methodString = "PUT";
3532         break;
3533 
3534       case OrthancPluginHttpMethod_Delete:
3535         methodString = "DELETE";
3536         break;
3537 
3538       default:
3539         throw OrthancException(ErrorCode_ParameterOutOfRange);
3540     }
3541 
3542     CLOG(INFO, PLUGINS) << "Plugin making REST " << methodString << " call to URI " << p.uri
3543                         << (p.afterPlugins ? " (after plugins)" : " (built-in API)");
3544 
3545     HttpToolbox::Arguments headers;
3546 
3547     for (uint32_t i = 0; i < p.headersCount; i++)
3548     {
3549       std::string name(p.headersKeys[i]);
3550       std::transform(name.begin(), name.end(), name.begin(), ::tolower);
3551       headers[name] = p.headersValues[i];
3552     }
3553 
3554     IHttpHandler* handler;
3555 
3556     {
3557       PImpl::ServerContextLock lock(*pimpl_);
3558       handler = &lock.GetContext().GetHttpHandler().RestrictToOrthancRestApi(!p.afterPlugins);
3559     }
3560 
3561     std::string answerBody;
3562     std::map<std::string, std::string> answerHeaders;
3563     HttpStatus status;
3564 
3565     switch (p.method)
3566     {
3567       case OrthancPluginHttpMethod_Get:
3568         status = IHttpHandler::SimpleGet(
3569           answerBody, &answerHeaders, *handler, RequestOrigin_Plugins, p.uri, headers);
3570         break;
3571 
3572       case OrthancPluginHttpMethod_Post:
3573         status = IHttpHandler::SimplePost(
3574           answerBody, &answerHeaders, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize, headers);
3575         break;
3576 
3577       case OrthancPluginHttpMethod_Put:
3578         status = IHttpHandler::SimplePut(
3579           answerBody, &answerHeaders, *handler, RequestOrigin_Plugins, p.uri, p.body, p.bodySize, headers);
3580         break;
3581 
3582       case OrthancPluginHttpMethod_Delete:
3583         status = IHttpHandler::SimpleDelete(
3584           &answerHeaders, *handler, RequestOrigin_Plugins, p.uri, headers);
3585         break;
3586 
3587       default:
3588         throw OrthancException(ErrorCode_ParameterOutOfRange);
3589     }
3590 
3591     *p.httpStatus = static_cast<uint16_t>(status);
3592 
3593     if (p.answerHeaders != NULL)
3594     {
3595       CopyDictionary(*p.answerHeaders, answerHeaders);
3596     }
3597 
3598     try
3599     {
3600       if (p.answerBody != NULL)
3601       {
3602         CopyToMemoryBuffer(*p.answerBody, answerBody);
3603       }
3604     }
3605     catch (OrthancException&)
3606     {
3607       if (p.answerHeaders != NULL)
3608       {
3609         free(p.answerHeaders->data);
3610       }
3611       throw;
3612     }
3613   }
3614 
3615 
CallPeerApi(const void * parameters)3616   void OrthancPlugins::CallPeerApi(const void* parameters)
3617   {
3618     const _OrthancPluginCallPeerApi& p = *reinterpret_cast<const _OrthancPluginCallPeerApi*>(parameters);
3619     const OrthancPeers& peers = *reinterpret_cast<const OrthancPeers*>(p.peers);
3620 
3621     HttpClient client(peers.GetPeerParameters(p.peerIndex), p.uri);
3622     client.SetConvertHeadersToLowerCase(false);
3623 
3624     if (p.timeout != 0)
3625     {
3626       client.SetTimeout(p.timeout);
3627     }
3628 
3629     for (uint32_t i = 0; i < p.additionalHeadersCount; i++)
3630     {
3631       if (p.additionalHeadersKeys[i] == NULL ||
3632           p.additionalHeadersValues[i] == NULL)
3633       {
3634         throw OrthancException(ErrorCode_NullPointer);
3635       }
3636 
3637       client.AddHeader(p.additionalHeadersKeys[i], p.additionalHeadersValues[i]);
3638     }
3639 
3640     switch (p.method)
3641     {
3642       case OrthancPluginHttpMethod_Get:
3643         client.SetMethod(HttpMethod_Get);
3644         break;
3645 
3646       case OrthancPluginHttpMethod_Post:
3647         client.SetMethod(HttpMethod_Post);
3648         client.SetExternalBody(p.body, p.bodySize);
3649         break;
3650 
3651       case OrthancPluginHttpMethod_Put:
3652         client.SetMethod(HttpMethod_Put);
3653         client.SetExternalBody(p.body, p.bodySize);
3654         break;
3655 
3656       case OrthancPluginHttpMethod_Delete:
3657         client.SetMethod(HttpMethod_Delete);
3658         break;
3659 
3660       default:
3661         throw OrthancException(ErrorCode_ParameterOutOfRange);
3662     }
3663 
3664     std::string body;
3665     HttpClient::HttpHeaders headers;
3666 
3667     bool success = client.Apply(body, headers);
3668 
3669     // The HTTP request has succeeded
3670     *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus());
3671 
3672     if (!success)
3673     {
3674       HttpClient::ThrowException(client.GetLastStatus());
3675     }
3676 
3677     // Copy the HTTP headers of the answer, if the plugin requested them
3678     if (p.answerHeaders != NULL)
3679     {
3680       CopyDictionary(*p.answerHeaders, headers);
3681     }
3682 
3683     // Copy the body of the answer if it makes sense
3684     if (p.method != OrthancPluginHttpMethod_Delete)
3685     {
3686       try
3687       {
3688         if (p.answerBody != NULL)
3689         {
3690           CopyToMemoryBuffer(*p.answerBody, body);
3691         }
3692       }
3693       catch (OrthancException&)
3694       {
3695         if (p.answerHeaders != NULL)
3696         {
3697           free(p.answerHeaders->data);
3698         }
3699         throw;
3700       }
3701     }
3702   }
3703 
3704 
ConvertPixelFormat(const void * parameters)3705   void OrthancPlugins::ConvertPixelFormat(const void* parameters)
3706   {
3707     const _OrthancPluginConvertPixelFormat& p = *reinterpret_cast<const _OrthancPluginConvertPixelFormat*>(parameters);
3708     const ImageAccessor& source = *reinterpret_cast<const ImageAccessor*>(p.source);
3709 
3710     std::unique_ptr<ImageAccessor> target(new Image(Plugins::Convert(p.targetFormat), source.GetWidth(), source.GetHeight(), false));
3711     ImageProcessing::Convert(*target, source);
3712 
3713     *(p.target) = ReturnImage(target);
3714   }
3715 
3716 
3717 
GetFontInfo(const void * parameters)3718   void OrthancPlugins::GetFontInfo(const void* parameters)
3719   {
3720     const _OrthancPluginGetFontInfo& p = *reinterpret_cast<const _OrthancPluginGetFontInfo*>(parameters);
3721 
3722     {
3723       OrthancConfiguration::ReaderLock lock;
3724 
3725       const Font& font = lock.GetConfiguration().GetFontRegistry().GetFont(p.fontIndex);
3726 
3727       if (p.name != NULL)
3728       {
3729         *(p.name) = font.GetName().c_str();
3730       }
3731       else if (p.size != NULL)
3732       {
3733         *(p.size) = font.GetSize();
3734       }
3735       else
3736       {
3737         throw OrthancException(ErrorCode_InternalError);
3738       }
3739     }
3740   }
3741 
3742 
DrawText(const void * parameters)3743   void OrthancPlugins::DrawText(const void* parameters)
3744   {
3745     const _OrthancPluginDrawText& p = *reinterpret_cast<const _OrthancPluginDrawText*>(parameters);
3746 
3747     {
3748       OrthancConfiguration::ReaderLock lock;
3749       const Font& font = lock.GetConfiguration().GetFontRegistry().GetFont(p.fontIndex);
3750 
3751       ImageAccessor& target = *reinterpret_cast<ImageAccessor*>(p.image);
3752       font.Draw(target, p.utf8Text, p.x, p.y, p.r, p.g, p.b);
3753     }
3754   }
3755 
3756 
ApplyDicomToJson(_OrthancPluginService service,const void * parameters)3757   void OrthancPlugins::ApplyDicomToJson(_OrthancPluginService service,
3758                                         const void* parameters)
3759   {
3760     const _OrthancPluginDicomToJson& p =
3761       *reinterpret_cast<const _OrthancPluginDicomToJson*>(parameters);
3762 
3763     std::unique_ptr<ParsedDicomFile> dicom;
3764 
3765     if (service == _OrthancPluginService_DicomBufferToJson)
3766     {
3767       dicom.reset(new ParsedDicomFile(p.buffer, p.size));
3768     }
3769     else
3770     {
3771       if (p.instanceId == NULL)
3772       {
3773         throw OrthancException(ErrorCode_NullPointer);
3774       }
3775 
3776       std::string content;
3777 
3778       {
3779         PImpl::ServerContextLock lock(*pimpl_);
3780         lock.GetContext().ReadDicom(content, p.instanceId);
3781       }
3782 
3783       dicom.reset(new ParsedDicomFile(content));
3784     }
3785 
3786     Json::Value json;
3787     dicom->DatasetToJson(json, Plugins::Convert(p.format),
3788                          static_cast<DicomToJsonFlags>(p.flags), p.maxStringLength);
3789 
3790     std::string s;
3791     Toolbox::WriteFastJson(s, json);
3792     *p.result = CopyString(s);
3793   }
3794 
3795 
ApplyCreateDicom(const _OrthancPluginCreateDicom & parameters,const char * privateCreatorC)3796   void OrthancPlugins::ApplyCreateDicom(const _OrthancPluginCreateDicom& parameters,
3797                                         const char* privateCreatorC)
3798   {
3799     Json::Value json;
3800 
3801     if (parameters.json == NULL)
3802     {
3803       json = Json::objectValue;
3804     }
3805     else if (!Toolbox::ReadJson(json, parameters.json))
3806     {
3807       throw OrthancException(ErrorCode_BadJson);
3808     }
3809 
3810     std::string dicom;
3811 
3812     {
3813       // Fix issue 168 (Plugins can't read private tags from the
3814       // configuration file)
3815       // https://bugs.orthanc-server.com/show_bug.cgi?id=168
3816       std::string privateCreator;
3817 
3818       if (privateCreatorC == NULL)
3819       {
3820         OrthancConfiguration::ReaderLock lock;
3821         privateCreator = lock.GetConfiguration().GetDefaultPrivateCreator();
3822       }
3823       else
3824       {
3825         // New in Orthanc 1.9.0
3826         privateCreator.assign(privateCreatorC);
3827       }
3828 
3829       std::unique_ptr<ParsedDicomFile> file
3830         (ParsedDicomFile::CreateFromJson(json, static_cast<DicomFromJsonFlags>(parameters.flags),
3831                                          privateCreator));
3832 
3833       if (parameters.pixelData)
3834       {
3835         file->EmbedImage(*reinterpret_cast<const ImageAccessor*>(parameters.pixelData));
3836       }
3837 
3838       file->SaveToMemoryBuffer(dicom);
3839     }
3840 
3841     CopyToMemoryBuffer(*parameters.target, dicom);
3842   }
3843 
3844 
ComputeHash(_OrthancPluginService service,const void * parameters)3845   void OrthancPlugins::ComputeHash(_OrthancPluginService service,
3846                                    const void* parameters)
3847   {
3848     const _OrthancPluginComputeHash& p =
3849       *reinterpret_cast<const _OrthancPluginComputeHash*>(parameters);
3850 
3851     std::string hash;
3852     switch (service)
3853     {
3854       case _OrthancPluginService_ComputeMd5:
3855         Toolbox::ComputeMD5(hash, p.buffer, p.size);
3856         break;
3857 
3858       case _OrthancPluginService_ComputeSha1:
3859         Toolbox::ComputeSHA1(hash, p.buffer, p.size);
3860         break;
3861 
3862       default:
3863         throw OrthancException(ErrorCode_ParameterOutOfRange);
3864     }
3865 
3866     *p.result = CopyString(hash);
3867   }
3868 
3869 
GetTagName(const void * parameters)3870   void OrthancPlugins::GetTagName(const void* parameters)
3871   {
3872     const _OrthancPluginGetTagName& p =
3873       *reinterpret_cast<const _OrthancPluginGetTagName*>(parameters);
3874 
3875     std::string privateCreator;
3876 
3877     if (p.privateCreator != NULL)
3878     {
3879       privateCreator = p.privateCreator;
3880     }
3881 
3882     DicomTag tag(p.group, p.element);
3883     *p.result = CopyString(FromDcmtkBridge::GetTagName(tag, privateCreator));
3884   }
3885 
3886 
ApplyCreateImage(_OrthancPluginService service,const void * parameters)3887   void OrthancPlugins::ApplyCreateImage(_OrthancPluginService service,
3888                                         const void* parameters)
3889   {
3890     const _OrthancPluginCreateImage& p =
3891       *reinterpret_cast<const _OrthancPluginCreateImage*>(parameters);
3892 
3893     std::unique_ptr<ImageAccessor> result;
3894 
3895     switch (service)
3896     {
3897       case _OrthancPluginService_CreateImage:
3898         result.reset(new Image(Plugins::Convert(p.format), p.width, p.height, false));
3899         break;
3900 
3901       case _OrthancPluginService_CreateImageAccessor:
3902         result.reset(new ImageAccessor);
3903         result->AssignWritable(Plugins::Convert(p.format), p.width, p.height, p.pitch, p.buffer);
3904         break;
3905 
3906       case _OrthancPluginService_DecodeDicomImage:
3907       {
3908         PImpl::ServerContextLock lock(*pimpl_);
3909         result.reset(lock.GetContext().DecodeDicomFrame(p.constBuffer, p.bufferSize, p.frameIndex));
3910         break;
3911       }
3912 
3913       default:
3914         throw OrthancException(ErrorCode_InternalError);
3915     }
3916 
3917     *(p.target) = ReturnImage(result);
3918   }
3919 
3920 
ApplySendMultipartItem(const void * parameters)3921   void OrthancPlugins::ApplySendMultipartItem(const void* parameters)
3922   {
3923     // An exception might be raised in this function if the
3924     // connection was closed by the HTTP client.
3925     const _OrthancPluginAnswerBuffer& p =
3926       *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters);
3927 
3928     std::map<std::string, std::string> headers;  // No custom headers
3929     reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->SendMultipartItem(p.answer, p.answerSize, headers);
3930   }
3931 
3932 
ApplySendMultipartItem2(const void * parameters)3933   void OrthancPlugins::ApplySendMultipartItem2(const void* parameters)
3934   {
3935     // An exception might be raised in this function if the
3936     // connection was closed by the HTTP client.
3937     const _OrthancPluginSendMultipartItem2& p =
3938       *reinterpret_cast<const _OrthancPluginSendMultipartItem2*>(parameters);
3939 
3940     std::map<std::string, std::string> headers;
3941     for (uint32_t i = 0; i < p.headersCount; i++)
3942     {
3943       headers[p.headersKeys[i]] = p.headersValues[i];
3944     }
3945 
3946     reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->SendMultipartItem(p.answer, p.answerSize, headers);
3947   }
3948 
3949 
DatabaseAnswer(const void * parameters)3950   void OrthancPlugins::DatabaseAnswer(const void* parameters)
3951   {
3952     const _OrthancPluginDatabaseAnswer& p =
3953       *reinterpret_cast<const _OrthancPluginDatabaseAnswer*>(parameters);
3954 
3955     if (pimpl_->database_.get() != NULL)
3956     {
3957       pimpl_->database_->AnswerReceived(p);
3958     }
3959     else
3960     {
3961       throw OrthancException(ErrorCode_BadRequest,
3962                              "Cannot invoke this service without a custom database back-end");
3963     }
3964   }
3965 
3966 
3967   namespace
3968   {
3969     class DictionaryReadLocker
3970     {
3971     private:
3972       const DcmDataDictionary& dictionary_;
3973 
3974     public:
DictionaryReadLocker()3975       DictionaryReadLocker() : dictionary_(dcmDataDict.rdlock())
3976       {
3977       }
3978 
~DictionaryReadLocker()3979       ~DictionaryReadLocker()
3980       {
3981 #if DCMTK_VERSION_NUMBER >= 364
3982         dcmDataDict.rdunlock();
3983 #else
3984         dcmDataDict.unlock();
3985 #endif
3986       }
3987 
operator ->()3988       const DcmDataDictionary* operator->()
3989       {
3990         return &dictionary_;
3991       }
3992     };
3993   }
3994 
3995 
ApplyLookupDictionary(const void * parameters)3996   void OrthancPlugins::ApplyLookupDictionary(const void* parameters)
3997   {
3998     const _OrthancPluginLookupDictionary& p =
3999       *reinterpret_cast<const _OrthancPluginLookupDictionary*>(parameters);
4000 
4001     DicomTag tag(FromDcmtkBridge::ParseTag(p.name));
4002     DcmTagKey tag2(tag.GetGroup(), tag.GetElement());
4003 
4004     DictionaryReadLocker locker;
4005     const DcmDictEntry* entry = NULL;
4006 
4007     if (tag.IsPrivate())
4008     {
4009       // Fix issue 168 (Plugins can't read private tags from the
4010       // configuration file)
4011       // https://bugs.orthanc-server.com/show_bug.cgi?id=168
4012       std::string privateCreator;
4013       {
4014         OrthancConfiguration::ReaderLock lock;
4015         privateCreator = lock.GetConfiguration().GetDefaultPrivateCreator();
4016       }
4017 
4018       entry = locker->findEntry(tag2, privateCreator.c_str());
4019     }
4020     else
4021     {
4022       entry = locker->findEntry(tag2, NULL);
4023     }
4024 
4025     if (entry == NULL)
4026     {
4027       throw OrthancException(ErrorCode_UnknownDicomTag);
4028     }
4029     else
4030     {
4031       p.target->group = entry->getKey().getGroup();
4032       p.target->element = entry->getKey().getElement();
4033       p.target->vr = Plugins::Convert(FromDcmtkBridge::Convert(entry->getEVR()));
4034       p.target->minMultiplicity = static_cast<uint32_t>(entry->getVMMin());
4035       p.target->maxMultiplicity = (entry->getVMMax() == DcmVariableVM ? 0 : static_cast<uint32_t>(entry->getVMMax()));
4036     }
4037   }
4038 
4039 
InvokeSafeService(SharedLibrary & plugin,_OrthancPluginService service,const void * parameters)4040   bool OrthancPlugins::InvokeSafeService(SharedLibrary& plugin,
4041                                          _OrthancPluginService service,
4042                                          const void* parameters)
4043   {
4044     // Services that can be run without mutual exclusion
4045 
4046     switch (service)
4047     {
4048       case _OrthancPluginService_GetOrthancPath:
4049       {
4050         std::string s = SystemToolbox::GetPathToExecutable();
4051         *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
4052         return true;
4053       }
4054 
4055       case _OrthancPluginService_GetOrthancDirectory:
4056       {
4057         std::string s = SystemToolbox::GetDirectoryOfExecutable();
4058         *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
4059         return true;
4060       }
4061 
4062       case _OrthancPluginService_GetConfigurationPath:
4063       {
4064         std::string s;
4065 
4066         {
4067           OrthancConfiguration::ReaderLock lock;
4068           s = lock.GetConfiguration().GetConfigurationAbsolutePath();
4069         }
4070 
4071         *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
4072         return true;
4073       }
4074 
4075       case _OrthancPluginService_GetConfiguration:
4076       {
4077         std::string s;
4078 
4079         {
4080           OrthancConfiguration::ReaderLock lock;
4081           lock.GetConfiguration().Format(s);
4082         }
4083 
4084         *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result = CopyString(s);
4085         return true;
4086       }
4087 
4088       case _OrthancPluginService_BufferCompression:
4089         BufferCompression(parameters);
4090         return true;
4091 
4092       case _OrthancPluginService_AnswerBuffer:
4093         AnswerBuffer(parameters);
4094         return true;
4095 
4096       case _OrthancPluginService_CompressAndAnswerPngImage:
4097         CompressAndAnswerPngImage(parameters);
4098         return true;
4099 
4100       case _OrthancPluginService_CompressAndAnswerImage:
4101         CompressAndAnswerImage(parameters);
4102         return true;
4103 
4104       case _OrthancPluginService_GetDicomForInstance:
4105         GetDicomForInstance(parameters);
4106         return true;
4107 
4108       case _OrthancPluginService_RestApiGet:
4109         RestApiGet(parameters, false);
4110         return true;
4111 
4112       case _OrthancPluginService_RestApiGetAfterPlugins:
4113         RestApiGet(parameters, true);
4114         return true;
4115 
4116       case _OrthancPluginService_RestApiGet2:
4117         RestApiGet2(parameters);
4118         return true;
4119 
4120       case _OrthancPluginService_RestApiPost:
4121         RestApiPostPut(true, parameters, false);
4122         return true;
4123 
4124       case _OrthancPluginService_RestApiPostAfterPlugins:
4125         RestApiPostPut(true, parameters, true);
4126         return true;
4127 
4128       case _OrthancPluginService_RestApiDelete:
4129         RestApiDelete(parameters, false);
4130         return true;
4131 
4132       case _OrthancPluginService_RestApiDeleteAfterPlugins:
4133         RestApiDelete(parameters, true);
4134         return true;
4135 
4136       case _OrthancPluginService_RestApiPut:
4137         RestApiPostPut(false, parameters, false);
4138         return true;
4139 
4140       case _OrthancPluginService_RestApiPutAfterPlugins:
4141         RestApiPostPut(false, parameters, true);
4142         return true;
4143 
4144       case _OrthancPluginService_Redirect:
4145         Redirect(parameters);
4146         return true;
4147 
4148       case _OrthancPluginService_SendUnauthorized:
4149         SendUnauthorized(parameters);
4150         return true;
4151 
4152       case _OrthancPluginService_SendMethodNotAllowed:
4153         SendMethodNotAllowed(parameters);
4154         return true;
4155 
4156       case _OrthancPluginService_SendHttpStatus:
4157         SendHttpStatus(parameters);
4158         return true;
4159 
4160       case _OrthancPluginService_SendHttpStatusCode:
4161         SendHttpStatusCode(parameters);
4162         return true;
4163 
4164       case _OrthancPluginService_SetCookie:
4165         SetCookie(parameters);
4166         return true;
4167 
4168       case _OrthancPluginService_SetHttpHeader:
4169         SetHttpHeader(parameters);
4170         return true;
4171 
4172       case _OrthancPluginService_SetHttpErrorDetails:
4173         SetHttpErrorDetails(parameters);
4174         return true;
4175 
4176       case _OrthancPluginService_LookupPatient:
4177       case _OrthancPluginService_LookupStudy:
4178       case _OrthancPluginService_LookupStudyWithAccessionNumber:
4179       case _OrthancPluginService_LookupSeries:
4180       case _OrthancPluginService_LookupInstance:
4181         LookupResource(service, parameters);
4182         return true;
4183 
4184       case _OrthancPluginService_GetInstanceRemoteAet:
4185       case _OrthancPluginService_GetInstanceSize:
4186       case _OrthancPluginService_GetInstanceData:
4187       case _OrthancPluginService_GetInstanceJson:
4188       case _OrthancPluginService_GetInstanceSimplifiedJson:
4189       case _OrthancPluginService_HasInstanceMetadata:
4190       case _OrthancPluginService_GetInstanceMetadata:
4191       case _OrthancPluginService_GetInstanceOrigin:
4192       case _OrthancPluginService_GetInstanceTransferSyntaxUid:
4193       case _OrthancPluginService_HasInstancePixelData:
4194         AccessDicomInstance(service, parameters);
4195         return true;
4196 
4197       case _OrthancPluginService_GetInstanceFramesCount:
4198       case _OrthancPluginService_GetInstanceRawFrame:
4199       case _OrthancPluginService_GetInstanceDecodedFrame:
4200       case _OrthancPluginService_SerializeDicomInstance:
4201       case _OrthancPluginService_GetInstanceAdvancedJson:
4202       case _OrthancPluginService_GetInstanceDicomWebJson:
4203       case _OrthancPluginService_GetInstanceDicomWebXml:
4204         AccessDicomInstance2(service, parameters);
4205         return true;
4206 
4207       case _OrthancPluginService_SetGlobalProperty:
4208       {
4209         const _OrthancPluginGlobalProperty& p =
4210           *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
4211         if (p.property < 1024)
4212         {
4213           return false;
4214         }
4215         else
4216         {
4217           // TODO - Plugins can only access global properties of their
4218           // own Orthanc server (no access to the shared global properties)
4219           PImpl::ServerContextLock lock(*pimpl_);
4220           lock.GetContext().GetIndex().SetGlobalProperty(static_cast<GlobalProperty>(p.property),
4221                                                          false /* not shared */, p.value);
4222           return true;
4223         }
4224       }
4225 
4226       case _OrthancPluginService_GetGlobalProperty:
4227       {
4228         const _OrthancPluginGlobalProperty& p =
4229           *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
4230 
4231         std::string result;
4232 
4233         {
4234           // TODO - Plugins can only access global properties of their
4235           // own Orthanc server (no access to the shared global properties)
4236           PImpl::ServerContextLock lock(*pimpl_);
4237           result = lock.GetContext().GetIndex().GetGlobalProperty(static_cast<GlobalProperty>(p.property),
4238                                                                   false /* not shared */, p.value);
4239         }
4240 
4241         *(p.result) = CopyString(result);
4242         return true;
4243       }
4244 
4245       case _OrthancPluginService_GetExpectedDatabaseVersion:
4246       {
4247         const _OrthancPluginReturnSingleValue& p =
4248           *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
4249         *(p.resultUint32) = ORTHANC_DATABASE_VERSION;
4250         return true;
4251       }
4252 
4253       case _OrthancPluginService_StartMultipartAnswer:
4254       {
4255         const _OrthancPluginStartMultipartAnswer& p =
4256           *reinterpret_cast<const _OrthancPluginStartMultipartAnswer*>(parameters);
4257         reinterpret_cast<PImpl::PluginHttpOutput*>(p.output)->StartMultipart(p.subType, p.contentType);
4258         return true;
4259       }
4260 
4261       case _OrthancPluginService_SendMultipartItem:
4262         ApplySendMultipartItem(parameters);
4263         return true;
4264 
4265       case _OrthancPluginService_SendMultipartItem2:
4266         ApplySendMultipartItem2(parameters);
4267         return true;
4268 
4269       case _OrthancPluginService_ReadFile:
4270       {
4271         const _OrthancPluginReadFile& p =
4272           *reinterpret_cast<const _OrthancPluginReadFile*>(parameters);
4273 
4274         std::string content;
4275         SystemToolbox::ReadFile(content, p.path);
4276         CopyToMemoryBuffer(*p.target, content.size() > 0 ? content.c_str() : NULL, content.size());
4277 
4278         return true;
4279       }
4280 
4281       case _OrthancPluginService_WriteFile:
4282       {
4283         const _OrthancPluginWriteFile& p =
4284           *reinterpret_cast<const _OrthancPluginWriteFile*>(parameters);
4285         SystemToolbox::WriteFile(p.data, p.size, p.path, true /* run fsync() */);
4286         return true;
4287       }
4288 
4289       case _OrthancPluginService_GetErrorDescription:
4290       {
4291         const _OrthancPluginGetErrorDescription& p =
4292           *reinterpret_cast<const _OrthancPluginGetErrorDescription*>(parameters);
4293         *(p.target) = EnumerationToString(static_cast<ErrorCode>(p.error));
4294         return true;
4295       }
4296 
4297       case _OrthancPluginService_GetImagePixelFormat:
4298       {
4299         const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
4300         *(p.resultPixelFormat) = Plugins::Convert(reinterpret_cast<const ImageAccessor*>(p.image)->GetFormat());
4301         return true;
4302       }
4303 
4304       case _OrthancPluginService_GetImageWidth:
4305       {
4306         const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
4307         *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetWidth();
4308         return true;
4309       }
4310 
4311       case _OrthancPluginService_GetImageHeight:
4312       {
4313         const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
4314         *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetHeight();
4315         return true;
4316       }
4317 
4318       case _OrthancPluginService_GetImagePitch:
4319       {
4320         const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
4321         *(p.resultUint32) = reinterpret_cast<const ImageAccessor*>(p.image)->GetPitch();
4322         return true;
4323       }
4324 
4325       case _OrthancPluginService_GetImageBuffer:
4326       {
4327         const _OrthancPluginGetImageInfo& p = *reinterpret_cast<const _OrthancPluginGetImageInfo*>(parameters);
4328         *(p.resultBuffer) = const_cast<void*>(reinterpret_cast<const ImageAccessor*>(p.image)->GetConstBuffer());
4329         return true;
4330       }
4331 
4332       case _OrthancPluginService_FreeImage:
4333       {
4334         const _OrthancPluginFreeImage& p = *reinterpret_cast<const _OrthancPluginFreeImage*>(parameters);
4335 
4336         if (p.image != NULL)
4337         {
4338           delete reinterpret_cast<ImageAccessor*>(p.image);
4339         }
4340 
4341         return true;
4342       }
4343 
4344       case _OrthancPluginService_UncompressImage:
4345         UncompressImage(parameters);
4346         return true;
4347 
4348       case _OrthancPluginService_CompressImage:
4349         CompressImage(parameters);
4350         return true;
4351 
4352       case _OrthancPluginService_CallHttpClient:
4353         CallHttpClient(parameters);
4354         return true;
4355 
4356       case _OrthancPluginService_CallHttpClient2:
4357         CallHttpClient2(parameters);
4358         return true;
4359 
4360       case _OrthancPluginService_ChunkedHttpClient:
4361         ChunkedHttpClient(parameters);
4362         return true;
4363 
4364       case _OrthancPluginService_CallRestApi:
4365         CallRestApi(parameters);
4366         return true;
4367 
4368       case _OrthancPluginService_ConvertPixelFormat:
4369         ConvertPixelFormat(parameters);
4370         return true;
4371 
4372       case _OrthancPluginService_GetFontsCount:
4373       {
4374         const _OrthancPluginReturnSingleValue& p =
4375           *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
4376 
4377         {
4378           OrthancConfiguration::ReaderLock lock;
4379           *(p.resultUint32) = lock.GetConfiguration().GetFontRegistry().GetSize();
4380         }
4381 
4382         return true;
4383       }
4384 
4385       case _OrthancPluginService_GetFontInfo:
4386         GetFontInfo(parameters);
4387         return true;
4388 
4389       case _OrthancPluginService_DrawText:
4390         DrawText(parameters);
4391         return true;
4392 
4393       case _OrthancPluginService_StorageAreaCreate:
4394       {
4395         const _OrthancPluginStorageAreaCreate& p =
4396           *reinterpret_cast<const _OrthancPluginStorageAreaCreate*>(parameters);
4397         IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
4398         storage.Create(p.uuid, p.content, static_cast<size_t>(p.size), Plugins::Convert(p.type));
4399         return true;
4400       }
4401 
4402       case _OrthancPluginService_StorageAreaRead:
4403       {
4404         const _OrthancPluginStorageAreaRead& p =
4405           *reinterpret_cast<const _OrthancPluginStorageAreaRead*>(parameters);
4406         IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
4407         std::unique_ptr<IMemoryBuffer> content(storage.Read(p.uuid, Plugins::Convert(p.type)));
4408         CopyToMemoryBuffer(*p.target, content->GetData(), content->GetSize());
4409         return true;
4410       }
4411 
4412       case _OrthancPluginService_StorageAreaRemove:
4413       {
4414         const _OrthancPluginStorageAreaRemove& p =
4415           *reinterpret_cast<const _OrthancPluginStorageAreaRemove*>(parameters);
4416         IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
4417         storage.Remove(p.uuid, Plugins::Convert(p.type));
4418         return true;
4419       }
4420 
4421       case _OrthancPluginService_DicomBufferToJson:
4422       case _OrthancPluginService_DicomInstanceToJson:
4423         ApplyDicomToJson(service, parameters);
4424         return true;
4425 
4426       case _OrthancPluginService_CreateDicom:
4427       {
4428         const _OrthancPluginCreateDicom& p =
4429           *reinterpret_cast<const _OrthancPluginCreateDicom*>(parameters);
4430         ApplyCreateDicom(p, NULL);
4431         return true;
4432       }
4433 
4434       case _OrthancPluginService_CreateDicom2:
4435       {
4436         // New in Orthanc 1.9.0
4437         const _OrthancPluginCreateDicom2& p =
4438           *reinterpret_cast<const _OrthancPluginCreateDicom2*>(parameters);
4439         ApplyCreateDicom(p.createDicom, p.privateCreator);
4440         return true;
4441       }
4442 
4443       case _OrthancPluginService_WorklistAddAnswer:
4444       {
4445         const _OrthancPluginWorklistAnswersOperation& p =
4446           *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
4447         reinterpret_cast<const WorklistHandler*>(p.query)->AddAnswer(p.answers, p.dicom, p.size);
4448         return true;
4449       }
4450 
4451       case _OrthancPluginService_WorklistMarkIncomplete:
4452       {
4453         const _OrthancPluginWorklistAnswersOperation& p =
4454           *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
4455         reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
4456         return true;
4457       }
4458 
4459       case _OrthancPluginService_WorklistIsMatch:
4460       {
4461         const _OrthancPluginWorklistQueryOperation& p =
4462           *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
4463         *p.isMatch = reinterpret_cast<const WorklistHandler*>(p.query)->IsMatch(p.dicom, p.size);
4464         return true;
4465       }
4466 
4467       case _OrthancPluginService_WorklistGetDicomQuery:
4468       {
4469         const _OrthancPluginWorklistQueryOperation& p =
4470           *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
4471         reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(*p.target);
4472         return true;
4473       }
4474 
4475       case _OrthancPluginService_FindAddAnswer:
4476       {
4477         const _OrthancPluginFindOperation& p =
4478           *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
4479         reinterpret_cast<DicomFindAnswers*>(p.answers)->Add(p.dicom, p.size);
4480         return true;
4481       }
4482 
4483       case _OrthancPluginService_FindMarkIncomplete:
4484       {
4485         const _OrthancPluginFindOperation& p =
4486           *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
4487         reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
4488         return true;
4489       }
4490 
4491       case _OrthancPluginService_GetFindQuerySize:
4492       case _OrthancPluginService_GetFindQueryTag:
4493       case _OrthancPluginService_GetFindQueryTagName:
4494       case _OrthancPluginService_GetFindQueryValue:
4495       {
4496         const _OrthancPluginFindOperation& p =
4497           *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
4498         reinterpret_cast<const FindHandler*>(p.query)->Invoke(service, p);
4499         return true;
4500       }
4501 
4502       case _OrthancPluginService_CreateImage:
4503       case _OrthancPluginService_CreateImageAccessor:
4504       case _OrthancPluginService_DecodeDicomImage:
4505         ApplyCreateImage(service, parameters);
4506         return true;
4507 
4508       case _OrthancPluginService_ComputeMd5:
4509       case _OrthancPluginService_ComputeSha1:
4510         ComputeHash(service, parameters);
4511         return true;
4512 
4513       case _OrthancPluginService_LookupDictionary:
4514         ApplyLookupDictionary(parameters);
4515         return true;
4516 
4517       case _OrthancPluginService_GenerateUuid:
4518       {
4519         *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters)->result =
4520           CopyString(Toolbox::GenerateUuid());
4521         return true;
4522       }
4523 
4524       case _OrthancPluginService_CreateFindMatcher:
4525       {
4526         const _OrthancPluginCreateFindMatcher& p =
4527           *reinterpret_cast<const _OrthancPluginCreateFindMatcher*>(parameters);
4528         ParsedDicomFile query(p.query, p.size);
4529         *(p.target) = reinterpret_cast<OrthancPluginFindMatcher*>(new HierarchicalMatcher(query));
4530         return true;
4531       }
4532 
4533       case _OrthancPluginService_FreeFindMatcher:
4534       {
4535         const _OrthancPluginFreeFindMatcher& p =
4536           *reinterpret_cast<const _OrthancPluginFreeFindMatcher*>(parameters);
4537 
4538         if (p.matcher != NULL)
4539         {
4540           delete reinterpret_cast<HierarchicalMatcher*>(p.matcher);
4541         }
4542 
4543         return true;
4544       }
4545 
4546       case _OrthancPluginService_FindMatcherIsMatch:
4547       {
4548         const _OrthancPluginFindMatcherIsMatch& p =
4549           *reinterpret_cast<const _OrthancPluginFindMatcherIsMatch*>(parameters);
4550 
4551         if (p.matcher == NULL)
4552         {
4553           throw OrthancException(ErrorCode_NullPointer);
4554         }
4555         else
4556         {
4557           ParsedDicomFile query(p.dicom, p.size);
4558           *p.isMatch = reinterpret_cast<const HierarchicalMatcher*>(p.matcher)->Match(query) ? 1 : 0;
4559           return true;
4560         }
4561       }
4562 
4563       case _OrthancPluginService_GetPeers:
4564       {
4565         const _OrthancPluginGetPeers& p =
4566           *reinterpret_cast<const _OrthancPluginGetPeers*>(parameters);
4567         *(p.peers) = reinterpret_cast<OrthancPluginPeers*>(new OrthancPeers);
4568         return true;
4569       }
4570 
4571       case _OrthancPluginService_FreePeers:
4572       {
4573         const _OrthancPluginFreePeers& p =
4574           *reinterpret_cast<const _OrthancPluginFreePeers*>(parameters);
4575 
4576         if (p.peers != NULL)
4577         {
4578           delete reinterpret_cast<OrthancPeers*>(p.peers);
4579         }
4580 
4581         return true;
4582       }
4583 
4584       case _OrthancPluginService_GetPeersCount:
4585       {
4586         const _OrthancPluginGetPeersCount& p =
4587           *reinterpret_cast<const _OrthancPluginGetPeersCount*>(parameters);
4588 
4589         if (p.peers == NULL)
4590         {
4591           throw OrthancException(ErrorCode_NullPointer);
4592         }
4593         else
4594         {
4595           *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeersCount();
4596           return true;
4597         }
4598       }
4599 
4600       case _OrthancPluginService_GetPeerName:
4601       {
4602         const _OrthancPluginGetPeerProperty& p =
4603           *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters);
4604 
4605         if (p.peers == NULL)
4606         {
4607           throw OrthancException(ErrorCode_NullPointer);
4608         }
4609         else
4610         {
4611           *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerName(p.peerIndex).c_str();
4612           return true;
4613         }
4614       }
4615 
4616       case _OrthancPluginService_GetPeerUrl:
4617       {
4618         const _OrthancPluginGetPeerProperty& p =
4619           *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters);
4620 
4621         if (p.peers == NULL)
4622         {
4623           throw OrthancException(ErrorCode_NullPointer);
4624         }
4625         else
4626         {
4627           *(p.target) = reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerParameters(p.peerIndex).GetUrl().c_str();
4628           return true;
4629         }
4630       }
4631 
4632       case _OrthancPluginService_GetPeerUserProperty:
4633       {
4634         const _OrthancPluginGetPeerProperty& p =
4635           *reinterpret_cast<const _OrthancPluginGetPeerProperty*>(parameters);
4636 
4637         if (p.peers == NULL ||
4638             p.userProperty == NULL)
4639         {
4640           throw OrthancException(ErrorCode_NullPointer);
4641         }
4642         else
4643         {
4644           const WebServiceParameters::Dictionary& properties =
4645             reinterpret_cast<const OrthancPeers*>(p.peers)->GetPeerParameters(p.peerIndex).GetUserProperties();
4646 
4647           WebServiceParameters::Dictionary::const_iterator found =
4648             properties.find(p.userProperty);
4649 
4650           if (found == properties.end())
4651           {
4652             *(p.target) = NULL;
4653           }
4654           else
4655           {
4656             *(p.target) = found->second.c_str();
4657           }
4658 
4659           return true;
4660         }
4661       }
4662 
4663       case _OrthancPluginService_CallPeerApi:
4664         CallPeerApi(parameters);
4665         return true;
4666 
4667       case _OrthancPluginService_CreateJob:
4668       {
4669         const _OrthancPluginCreateJob& p =
4670           *reinterpret_cast<const _OrthancPluginCreateJob*>(parameters);
4671         *(p.target) = reinterpret_cast<OrthancPluginJob*>(new PluginsJob(p));
4672         return true;
4673       }
4674 
4675       case _OrthancPluginService_FreeJob:
4676       {
4677         const _OrthancPluginFreeJob& p =
4678           *reinterpret_cast<const _OrthancPluginFreeJob*>(parameters);
4679 
4680         if (p.job != NULL)
4681         {
4682           delete reinterpret_cast<PluginsJob*>(p.job);
4683         }
4684 
4685         return true;
4686       }
4687 
4688       case _OrthancPluginService_SubmitJob:
4689       {
4690         const _OrthancPluginSubmitJob& p =
4691           *reinterpret_cast<const _OrthancPluginSubmitJob*>(parameters);
4692 
4693         std::string uuid;
4694 
4695         PImpl::ServerContextLock lock(*pimpl_);
4696         lock.GetContext().GetJobsEngine().GetRegistry().Submit
4697           (uuid, reinterpret_cast<PluginsJob*>(p.job), p.priority);
4698 
4699         *p.resultId = CopyString(uuid);
4700 
4701         return true;
4702       }
4703 
4704       case _OrthancPluginService_AutodetectMimeType:
4705       {
4706         const _OrthancPluginRetrieveStaticString& p =
4707           *reinterpret_cast<const _OrthancPluginRetrieveStaticString*>(parameters);
4708         *p.result = EnumerationToString(SystemToolbox::AutodetectMimeType(p.argument));
4709         return true;
4710       }
4711 
4712       case _OrthancPluginService_SetMetricsValue:
4713       {
4714         const _OrthancPluginSetMetricsValue& p =
4715           *reinterpret_cast<const _OrthancPluginSetMetricsValue*>(parameters);
4716 
4717         MetricsType type;
4718         switch (p.type)
4719         {
4720           case OrthancPluginMetricsType_Default:
4721             type = MetricsType_Default;
4722             break;
4723 
4724           case OrthancPluginMetricsType_Timer:
4725             type = MetricsType_MaxOver10Seconds;
4726             break;
4727 
4728           default:
4729             throw OrthancException(ErrorCode_ParameterOutOfRange);
4730         }
4731 
4732         {
4733           PImpl::ServerContextLock lock(*pimpl_);
4734           lock.GetContext().GetMetricsRegistry().SetValue(p.name, p.value, type);
4735         }
4736 
4737         return true;
4738       }
4739 
4740       case _OrthancPluginService_EncodeDicomWebJson:
4741       case _OrthancPluginService_EncodeDicomWebXml:
4742       {
4743         const _OrthancPluginEncodeDicomWeb& p =
4744           *reinterpret_cast<const _OrthancPluginEncodeDicomWeb*>(parameters);
4745 
4746         DicomWebBinaryFormatter formatter(p.callback);
4747         formatter.Apply(p.target,
4748                         (service == _OrthancPluginService_EncodeDicomWebJson),
4749                         p.dicom, p.dicomSize);
4750         return true;
4751       }
4752 
4753       case _OrthancPluginService_EncodeDicomWebJson2:
4754       case _OrthancPluginService_EncodeDicomWebXml2:
4755       {
4756         const _OrthancPluginEncodeDicomWeb2& p =
4757           *reinterpret_cast<const _OrthancPluginEncodeDicomWeb2*>(parameters);
4758 
4759         DicomWebBinaryFormatter formatter(p.callback, p.payload);
4760         formatter.Apply(p.target,
4761                         (service == _OrthancPluginService_EncodeDicomWebJson2),
4762                         p.dicom, p.dicomSize);
4763         return true;
4764       }
4765 
4766       case _OrthancPluginService_GetTagName:
4767         GetTagName(parameters);
4768         return true;
4769 
4770       case _OrthancPluginService_CreateDicomInstance:
4771       {
4772         const _OrthancPluginCreateDicomInstance& p =
4773           *reinterpret_cast<const _OrthancPluginCreateDicomInstance*>(parameters);
4774         *(p.target) = reinterpret_cast<OrthancPluginDicomInstance*>(
4775           new DicomInstanceFromBuffer(p.buffer, p.size));
4776         return true;
4777       }
4778 
4779       case _OrthancPluginService_FreeDicomInstance:
4780       {
4781         const _OrthancPluginFreeDicomInstance& p =
4782           *reinterpret_cast<const _OrthancPluginFreeDicomInstance*>(parameters);
4783 
4784         if (p.dicom != NULL)
4785         {
4786           IDicomInstance* obj = reinterpret_cast<IDicomInstance*>(p.dicom);
4787 
4788           if (obj->CanBeFreed())
4789           {
4790             delete obj;
4791           }
4792           else
4793           {
4794             throw OrthancException(ErrorCode_Plugin, "Cannot free a DICOM instance provided to a callback");
4795           }
4796         }
4797 
4798         return true;
4799       }
4800 
4801       case _OrthancPluginService_TranscodeDicomInstance:
4802       {
4803         const _OrthancPluginCreateDicomInstance& p =
4804           *reinterpret_cast<const _OrthancPluginCreateDicomInstance*>(parameters);
4805 
4806         DicomTransferSyntax transferSyntax;
4807         if (p.transferSyntax == NULL ||
4808             !LookupTransferSyntax(transferSyntax, p.transferSyntax))
4809         {
4810           throw OrthancException(ErrorCode_ParameterOutOfRange, "Unsupported transfer syntax: " +
4811                                  std::string(p.transferSyntax == NULL ? "(null)" : p.transferSyntax));
4812         }
4813         else
4814         {
4815           std::set<DicomTransferSyntax> syntaxes;
4816           syntaxes.insert(transferSyntax);
4817 
4818           IDicomTranscoder::DicomImage source;
4819           source.SetExternalBuffer(p.buffer, p.size);
4820 
4821           IDicomTranscoder::DicomImage transcoded;
4822           bool success;
4823 
4824           {
4825             PImpl::ServerContextLock lock(*pimpl_);
4826             success = lock.GetContext().Transcode(
4827               transcoded, source, syntaxes, true /* allow new sop */);
4828           }
4829 
4830           if (success)
4831           {
4832             *(p.target) = reinterpret_cast<OrthancPluginDicomInstance*>(
4833               new DicomInstanceFromTranscoded(transcoded));
4834             return true;
4835           }
4836           else
4837           {
4838             throw OrthancException(ErrorCode_NotImplemented, "Cannot transcode image");
4839           }
4840         }
4841       }
4842 
4843       case _OrthancPluginService_CreateMemoryBuffer:
4844       {
4845         const _OrthancPluginCreateMemoryBuffer& p =
4846           *reinterpret_cast<const _OrthancPluginCreateMemoryBuffer*>(parameters);
4847 
4848         p.target->data = NULL;
4849         p.target->size = 0;
4850 
4851         if (p.size != 0)
4852         {
4853           p.target->data = malloc(p.size);
4854           if (p.target->data == NULL)
4855           {
4856             throw OrthancException(ErrorCode_NotEnoughMemory);
4857           }
4858 
4859           p.target->size = p.size;
4860         }
4861 
4862         return true;
4863       }
4864 
4865       case _OrthancPluginService_CreateMemoryBuffer64:
4866       {
4867         const _OrthancPluginCreateMemoryBuffer64& p =
4868           *reinterpret_cast<const _OrthancPluginCreateMemoryBuffer64*>(parameters);
4869 
4870         p.target->data = NULL;
4871         p.target->size = 0;
4872 
4873         if (p.size != 0)
4874         {
4875           p.target->data = malloc(p.size);
4876           if (p.target->data == NULL)
4877           {
4878             throw OrthancException(ErrorCode_NotEnoughMemory);
4879           }
4880 
4881           p.target->size = p.size;
4882         }
4883 
4884         return true;
4885       }
4886 
4887       case _OrthancPluginService_RegisterIncomingHttpRequestFilter:
4888         RegisterIncomingHttpRequestFilter(parameters);
4889         return true;
4890 
4891       case _OrthancPluginService_RegisterIncomingHttpRequestFilter2:
4892         RegisterIncomingHttpRequestFilter2(parameters);
4893         return true;
4894 
4895       default:
4896         return false;
4897     }
4898   }
4899 
4900 
4901 
InvokeProtectedService(SharedLibrary & plugin,_OrthancPluginService service,const void * parameters)4902   bool OrthancPlugins::InvokeProtectedService(SharedLibrary& plugin,
4903                                               _OrthancPluginService service,
4904                                               const void* parameters)
4905   {
4906     // Services that must be run in mutual exclusion. Guideline:
4907     // Whenever "pimpl_" is directly accessed by the service, it
4908     // should be listed here.
4909 
4910     switch (service)
4911     {
4912       case _OrthancPluginService_RegisterRestCallback:
4913         RegisterRestCallback(parameters, true);
4914         return true;
4915 
4916       case _OrthancPluginService_RegisterRestCallbackNoLock:
4917         RegisterRestCallback(parameters, false);
4918         return true;
4919 
4920       case _OrthancPluginService_RegisterChunkedRestCallback:
4921         RegisterChunkedRestCallback(parameters);
4922         return true;
4923 
4924       case _OrthancPluginService_RegisterOnStoredInstanceCallback:
4925         RegisterOnStoredInstanceCallback(parameters);
4926         return true;
4927 
4928       case _OrthancPluginService_RegisterOnChangeCallback:
4929         RegisterOnChangeCallback(parameters);
4930         return true;
4931 
4932       case _OrthancPluginService_RegisterWorklistCallback:
4933         RegisterWorklistCallback(parameters);
4934         return true;
4935 
4936       case _OrthancPluginService_RegisterFindCallback:
4937         RegisterFindCallback(parameters);
4938         return true;
4939 
4940       case _OrthancPluginService_RegisterMoveCallback:
4941         RegisterMoveCallback(parameters);
4942         return true;
4943 
4944       case _OrthancPluginService_RegisterDecodeImageCallback:
4945         RegisterDecodeImageCallback(parameters);
4946         return true;
4947 
4948       case _OrthancPluginService_RegisterTranscoderCallback:
4949         RegisterTranscoderCallback(parameters);
4950         return true;
4951 
4952       case _OrthancPluginService_RegisterJobsUnserializer:
4953         RegisterJobsUnserializer(parameters);
4954         return true;
4955 
4956       case _OrthancPluginService_RegisterIncomingDicomInstanceFilter:
4957         RegisterIncomingDicomInstanceFilter(parameters);
4958         return true;
4959 
4960       case _OrthancPluginService_RegisterRefreshMetricsCallback:
4961         RegisterRefreshMetricsCallback(parameters);
4962         return true;
4963 
4964       case _OrthancPluginService_RegisterStorageCommitmentScpCallback:
4965         RegisterStorageCommitmentScpCallback(parameters);
4966         return true;
4967 
4968       case _OrthancPluginService_RegisterStorageArea:
4969       case _OrthancPluginService_RegisterStorageArea2:
4970       {
4971         CLOG(INFO, PLUGINS) << "Plugin has registered a custom storage area";
4972 
4973         if (pimpl_->storageArea_.get() == NULL)
4974         {
4975           if (service == _OrthancPluginService_RegisterStorageArea)
4976           {
4977             const _OrthancPluginRegisterStorageArea& p =
4978               *reinterpret_cast<const _OrthancPluginRegisterStorageArea*>(parameters);
4979             pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary()));
4980           }
4981           else if (service == _OrthancPluginService_RegisterStorageArea2)
4982           {
4983             const _OrthancPluginRegisterStorageArea2& p =
4984               *reinterpret_cast<const _OrthancPluginRegisterStorageArea2*>(parameters);
4985             pimpl_->storageArea_.reset(new StorageAreaFactory(plugin, p, GetErrorDictionary()));
4986           }
4987           else
4988           {
4989             throw OrthancException(ErrorCode_InternalError);
4990           }
4991         }
4992         else
4993         {
4994           throw OrthancException(ErrorCode_StorageAreaAlreadyRegistered);
4995         }
4996 
4997         return true;
4998       }
4999 
5000       case _OrthancPluginService_SetPluginProperty:
5001       {
5002         const _OrthancPluginSetPluginProperty& p =
5003           *reinterpret_cast<const _OrthancPluginSetPluginProperty*>(parameters);
5004         pimpl_->properties_[std::make_pair(p.plugin, p.property)] = p.value;
5005         return true;
5006       }
5007 
5008       case _OrthancPluginService_GetCommandLineArgumentsCount:
5009       {
5010         const _OrthancPluginReturnSingleValue& p =
5011           *reinterpret_cast<const _OrthancPluginReturnSingleValue*>(parameters);
5012         *(p.resultUint32) = pimpl_->argc_ - 1;
5013         return true;
5014       }
5015 
5016       case _OrthancPluginService_GetCommandLineArgument:
5017       {
5018         const _OrthancPluginGlobalProperty& p =
5019           *reinterpret_cast<const _OrthancPluginGlobalProperty*>(parameters);
5020 
5021         if (p.property + 1 > pimpl_->argc_)
5022         {
5023           return false;
5024         }
5025         else
5026         {
5027           std::string arg = std::string(pimpl_->argv_[p.property + 1]);
5028           *(p.result) = CopyString(arg);
5029           return true;
5030         }
5031       }
5032 
5033       case _OrthancPluginService_RegisterDatabaseBackend:
5034       {
5035         LOG(WARNING) << "Performance warning: Plugin has registered a custom database back-end with an old API";
5036         LOG(WARNING) << "The database backend has *no* support for revisions of metadata and attachments";
5037 
5038         const _OrthancPluginRegisterDatabaseBackend& p =
5039           *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters);
5040 
5041         if (pimpl_->database_.get() == NULL &&
5042             pimpl_->databaseV3_.get() == NULL)
5043         {
5044           pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
5045                                                             *p.backend, NULL, 0, p.payload));
5046         }
5047         else
5048         {
5049           throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
5050         }
5051 
5052         *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
5053 
5054         return true;
5055       }
5056 
5057       case _OrthancPluginService_RegisterDatabaseBackendV2:
5058       {
5059         LOG(WARNING) << "Performance warning: Plugin has registered a custom database back-end with an old API";
5060         LOG(WARNING) << "The database backend has *no* support for revisions of metadata and attachments";
5061 
5062         const _OrthancPluginRegisterDatabaseBackendV2& p =
5063           *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters);
5064 
5065         if (pimpl_->database_.get() == NULL &&
5066             pimpl_->databaseV3_.get() == NULL)
5067         {
5068           pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(),
5069                                                             *p.backend, p.extensions,
5070                                                             p.extensionsSize, p.payload));
5071         }
5072         else
5073         {
5074           throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
5075         }
5076 
5077         *(p.result) = reinterpret_cast<OrthancPluginDatabaseContext*>(pimpl_->database_.get());
5078 
5079         return true;
5080       }
5081 
5082       case _OrthancPluginService_RegisterDatabaseBackendV3:
5083       {
5084         CLOG(INFO, PLUGINS) << "Plugin has registered a custom database back-end";
5085 
5086         const _OrthancPluginRegisterDatabaseBackendV3& p =
5087           *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV3*>(parameters);
5088 
5089         if (pimpl_->database_.get() == NULL &&
5090             pimpl_->databaseV3_.get() == NULL)
5091         {
5092           pimpl_->databaseV3_.reset(new OrthancPluginDatabaseV3(plugin, GetErrorDictionary(), p.backend,
5093                                                                 p.backendSize, p.database, pimpl_->databaseServerIdentifier_));
5094           pimpl_->maxDatabaseRetries_ = p.maxDatabaseRetries;
5095         }
5096         else
5097         {
5098           throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered);
5099         }
5100 
5101         return true;
5102       }
5103 
5104       case _OrthancPluginService_DatabaseAnswer:
5105         throw OrthancException(ErrorCode_InternalError);   // Implemented before locking (*)
5106 
5107       case _OrthancPluginService_RegisterErrorCode:
5108       {
5109         const _OrthancPluginRegisterErrorCode& p =
5110           *reinterpret_cast<const _OrthancPluginRegisterErrorCode*>(parameters);
5111         *(p.target) = pimpl_->dictionary_.Register(plugin, p.code, p.httpStatus, p.message);
5112         return true;
5113       }
5114 
5115       case _OrthancPluginService_RegisterDictionaryTag:
5116       {
5117         const _OrthancPluginRegisterDictionaryTag& p =
5118           *reinterpret_cast<const _OrthancPluginRegisterDictionaryTag*>(parameters);
5119         FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element),
5120                                                Plugins::Convert(p.vr), p.name,
5121                                                p.minMultiplicity, p.maxMultiplicity, "");
5122         return true;
5123       }
5124 
5125       case _OrthancPluginService_RegisterPrivateDictionaryTag:
5126       {
5127         const _OrthancPluginRegisterPrivateDictionaryTag& p =
5128           *reinterpret_cast<const _OrthancPluginRegisterPrivateDictionaryTag*>(parameters);
5129         FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element),
5130                                                Plugins::Convert(p.vr), p.name,
5131                                                p.minMultiplicity, p.maxMultiplicity, p.privateCreator);
5132         return true;
5133       }
5134 
5135       case _OrthancPluginService_ReconstructMainDicomTags:
5136       {
5137         const _OrthancPluginReconstructMainDicomTags& p =
5138           *reinterpret_cast<const _OrthancPluginReconstructMainDicomTags*>(parameters);
5139 
5140         if (pimpl_->database_.get() == NULL)
5141         {
5142           throw OrthancException(ErrorCode_DatabasePlugin,
5143                                  "The service ReconstructMainDicomTags can only be invoked by custom database plugins");
5144         }
5145 
5146         VoidDatabaseListener listener;
5147 
5148         {
5149           IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea);
5150 
5151           std::unique_ptr<IDatabaseWrapper::ITransaction> transaction(
5152             pimpl_->database_->StartTransaction(TransactionType_ReadWrite, listener));
5153           ServerToolbox::ReconstructMainDicomTags(*transaction, storage, Plugins::Convert(p.level));
5154           transaction->Commit(0);
5155         }
5156 
5157         return true;
5158       }
5159 
5160       case _OrthancPluginService_GenerateRestApiAuthorizationToken:
5161       {
5162         const _OrthancPluginRetrieveDynamicString& p =
5163           *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters);
5164         const std::string token = Toolbox::GenerateUuid();
5165 
5166         pimpl_->authorizationTokens_.insert(token);
5167         *p.result = CopyString("Bearer " + token);
5168 
5169         return true;
5170       }
5171 
5172       default:
5173       {
5174         // This service is unknown to the Orthanc plugin engine
5175         return false;
5176       }
5177     }
5178   }
5179 
5180 
5181 
InvokeService(SharedLibrary & plugin,_OrthancPluginService service,const void * parameters)5182   bool OrthancPlugins::InvokeService(SharedLibrary& plugin,
5183                                      _OrthancPluginService service,
5184                                      const void* parameters)
5185   {
5186     CLOG(TRACE, PLUGINS) << "Calling service " << service << " from plugin " << plugin.GetPath();
5187 
5188     if (service == _OrthancPluginService_DatabaseAnswer)
5189     {
5190       // This case solves a deadlock at (*) reported by James Webster
5191       // on 2015-10-27 that was present in versions of Orthanc <=
5192       // 0.9.4 and related to database plugins implementing a custom
5193       // index. The problem was that locking the database is already
5194       // ensured by the "ServerIndex" class if the invoked service is
5195       // "DatabaseAnswer".
5196       DatabaseAnswer(parameters);
5197       return true;
5198     }
5199 
5200     if (InvokeSafeService(plugin, service, parameters))
5201     {
5202       // The invoked service does not require locking
5203       return true;
5204     }
5205     else
5206     {
5207       // The invoked service requires locking
5208       boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);   // (*)
5209       return InvokeProtectedService(plugin, service, parameters);
5210     }
5211   }
5212 
5213 
HasStorageArea() const5214   bool OrthancPlugins::HasStorageArea() const
5215   {
5216     boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
5217     return pimpl_->storageArea_.get() != NULL;
5218   }
5219 
HasDatabaseBackend() const5220   bool OrthancPlugins::HasDatabaseBackend() const
5221   {
5222     boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
5223     return (pimpl_->database_.get() != NULL ||
5224             pimpl_->databaseV3_.get() != NULL);
5225   }
5226 
5227 
CreateStorageArea()5228   IStorageArea* OrthancPlugins::CreateStorageArea()
5229   {
5230     if (!HasStorageArea())
5231     {
5232       throw OrthancException(ErrorCode_BadSequenceOfCalls);
5233     }
5234     else
5235     {
5236       return pimpl_->storageArea_->Create();
5237     }
5238   }
5239 
5240 
GetStorageAreaLibrary() const5241   const SharedLibrary& OrthancPlugins::GetStorageAreaLibrary() const
5242   {
5243     if (!HasStorageArea())
5244     {
5245       throw OrthancException(ErrorCode_BadSequenceOfCalls);
5246     }
5247     else
5248     {
5249       return pimpl_->storageArea_->GetSharedLibrary();
5250     }
5251   }
5252 
5253 
GetDatabaseBackend()5254   IDatabaseWrapper& OrthancPlugins::GetDatabaseBackend()
5255   {
5256     if (pimpl_->database_.get() != NULL)
5257     {
5258       return *pimpl_->database_;
5259     }
5260     else if (pimpl_->databaseV3_.get() != NULL)
5261     {
5262       return *pimpl_->databaseV3_;
5263     }
5264     else
5265     {
5266       throw OrthancException(ErrorCode_BadSequenceOfCalls);
5267     }
5268   }
5269 
5270 
GetDatabaseBackendLibrary() const5271   const SharedLibrary& OrthancPlugins::GetDatabaseBackendLibrary() const
5272   {
5273     if (pimpl_->database_.get() != NULL)
5274     {
5275       return pimpl_->database_->GetSharedLibrary();
5276     }
5277     else if (pimpl_->databaseV3_.get() != NULL)
5278     {
5279       return pimpl_->databaseV3_->GetSharedLibrary();
5280     }
5281     else
5282     {
5283       throw OrthancException(ErrorCode_BadSequenceOfCalls);
5284     }
5285   }
5286 
5287 
GetProperty(const char * plugin,_OrthancPluginProperty property) const5288   const char* OrthancPlugins::GetProperty(const char* plugin,
5289                                           _OrthancPluginProperty property) const
5290   {
5291     PImpl::Property p = std::make_pair(plugin, property);
5292     PImpl::Properties::const_iterator it = pimpl_->properties_.find(p);
5293 
5294     if (it == pimpl_->properties_.end())
5295     {
5296       return NULL;
5297     }
5298     else
5299     {
5300       return it->second.c_str();
5301     }
5302   }
5303 
5304 
SetCommandLineArguments(int argc,char * argv[])5305   void OrthancPlugins::SetCommandLineArguments(int argc, char* argv[])
5306   {
5307     if (argc < 1 || argv == NULL)
5308     {
5309       throw OrthancException(ErrorCode_ParameterOutOfRange);
5310     }
5311 
5312     pimpl_->argc_ = argc;
5313     pimpl_->argv_ = argv;
5314   }
5315 
5316 
GetManager()5317   PluginsManager& OrthancPlugins::GetManager()
5318   {
5319     return pimpl_->manager_;
5320   }
5321 
5322 
GetManager() const5323   const PluginsManager& OrthancPlugins::GetManager() const
5324   {
5325     return pimpl_->manager_;
5326   }
5327 
5328 
GetErrorDictionary()5329   PluginsErrorDictionary&  OrthancPlugins::GetErrorDictionary()
5330   {
5331     return pimpl_->dictionary_;
5332   }
5333 
5334 
ConstructWorklistRequestHandler()5335   IWorklistRequestHandler* OrthancPlugins::ConstructWorklistRequestHandler()
5336   {
5337     if (HasWorklistHandler())
5338     {
5339       return new WorklistHandler(*this);
5340     }
5341     else
5342     {
5343       return NULL;
5344     }
5345   }
5346 
5347 
HasWorklistHandler()5348   bool OrthancPlugins::HasWorklistHandler()
5349   {
5350     boost::mutex::scoped_lock lock(pimpl_->worklistCallbackMutex_);
5351     return pimpl_->worklistCallback_ != NULL;
5352   }
5353 
5354 
ConstructFindRequestHandler()5355   IFindRequestHandler* OrthancPlugins::ConstructFindRequestHandler()
5356   {
5357     if (HasFindHandler())
5358     {
5359       return new FindHandler(*this);
5360     }
5361     else
5362     {
5363       return NULL;
5364     }
5365   }
5366 
5367 
HasFindHandler()5368   bool OrthancPlugins::HasFindHandler()
5369   {
5370     boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_);
5371     return pimpl_->findCallback_ != NULL;
5372   }
5373 
5374 
ConstructMoveRequestHandler()5375   IMoveRequestHandler* OrthancPlugins::ConstructMoveRequestHandler()
5376   {
5377     if (HasMoveHandler())
5378     {
5379       return new MoveHandler(*this);
5380     }
5381     else
5382     {
5383       return NULL;
5384     }
5385   }
5386 
5387 
HasMoveHandler()5388   bool OrthancPlugins::HasMoveHandler()
5389   {
5390     boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
5391     return pimpl_->moveCallbacks_.callback != NULL;
5392   }
5393 
5394 
HasCustomImageDecoder()5395   bool OrthancPlugins::HasCustomImageDecoder()
5396   {
5397     boost::shared_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
5398     return !pimpl_->decodeImageCallbacks_.empty();
5399   }
5400 
5401 
HasCustomTranscoder()5402   bool OrthancPlugins::HasCustomTranscoder()
5403   {
5404     boost::shared_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
5405     return !pimpl_->transcoderCallbacks_.empty();
5406   }
5407 
5408 
Decode(const void * dicom,size_t size,unsigned int frame)5409   ImageAccessor* OrthancPlugins::Decode(const void* dicom,
5410                                         size_t size,
5411                                         unsigned int frame)
5412   {
5413     boost::shared_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
5414 
5415     for (PImpl::DecodeImageCallbacks::const_iterator
5416            decoder = pimpl_->decodeImageCallbacks_.begin();
5417          decoder != pimpl_->decodeImageCallbacks_.end(); ++decoder)
5418     {
5419       OrthancPluginImage* pluginImage = NULL;
5420       if ((*decoder) (&pluginImage, dicom, size, frame) == OrthancPluginErrorCode_Success &&
5421           pluginImage != NULL)
5422       {
5423         return reinterpret_cast<ImageAccessor*>(pluginImage);
5424       }
5425     }
5426 
5427     return NULL;
5428   }
5429 
5430 
IsAllowed(HttpMethod method,const char * uri,const char * ip,const char * username,const HttpToolbox::Arguments & httpHeaders,const HttpToolbox::GetArguments & getArguments)5431   bool OrthancPlugins::IsAllowed(HttpMethod method,
5432                                  const char* uri,
5433                                  const char* ip,
5434                                  const char* username,
5435                                  const HttpToolbox::Arguments& httpHeaders,
5436                                  const HttpToolbox::GetArguments& getArguments)
5437   {
5438     OrthancPluginHttpMethod cMethod = Plugins::Convert(method);
5439 
5440     std::vector<const char*> httpKeys(httpHeaders.size());
5441     std::vector<const char*> httpValues(httpHeaders.size());
5442 
5443     size_t pos = 0;
5444     for (HttpToolbox::Arguments::const_iterator
5445            it = httpHeaders.begin(); it != httpHeaders.end(); ++it, pos++)
5446     {
5447       httpKeys[pos] = it->first.c_str();
5448       httpValues[pos] = it->second.c_str();
5449     }
5450 
5451     std::vector<const char*> getKeys(getArguments.size());
5452     std::vector<const char*> getValues(getArguments.size());
5453 
5454     for (size_t i = 0; i < getArguments.size(); i++)
5455     {
5456       getKeys[i] = getArguments[i].first.c_str();
5457       getValues[i] = getArguments[i].second.c_str();
5458     }
5459 
5460     {
5461       boost::shared_lock<boost::shared_mutex> lock(pimpl_->incomingHttpRequestFilterMutex_);
5462 
5463       // Improved callback with support for GET arguments, since Orthanc 1.3.0
5464       for (PImpl::IncomingHttpRequestFilters2::const_iterator
5465              filter = pimpl_->incomingHttpRequestFilters2_.begin();
5466            filter != pimpl_->incomingHttpRequestFilters2_.end(); ++filter)
5467       {
5468         int32_t allowed = (*filter) (cMethod, uri, ip,
5469                                      httpKeys.size(),
5470                                      httpKeys.empty() ? NULL : &httpKeys[0],
5471                                      httpValues.empty() ? NULL : &httpValues[0],
5472                                      getKeys.size(),
5473                                      getKeys.empty() ? NULL : &getKeys[0],
5474                                      getValues.empty() ? NULL : &getValues[0]);
5475 
5476         if (allowed == 0)
5477         {
5478           return false;
5479         }
5480         else if (allowed != 1)
5481         {
5482           // The callback is only allowed to answer 0 or 1
5483           throw OrthancException(ErrorCode_Plugin);
5484         }
5485       }
5486 
5487       for (PImpl::IncomingHttpRequestFilters::const_iterator
5488              filter = pimpl_->incomingHttpRequestFilters_.begin();
5489            filter != pimpl_->incomingHttpRequestFilters_.end(); ++filter)
5490       {
5491         int32_t allowed = (*filter) (cMethod, uri, ip, httpKeys.size(),
5492                                      httpKeys.empty() ? NULL : &httpKeys[0],
5493                                      httpValues.empty() ? NULL : &httpValues[0]);
5494 
5495         if (allowed == 0)
5496         {
5497           return false;
5498         }
5499         else if (allowed != 1)
5500         {
5501           // The callback is only allowed to answer 0 or 1
5502           throw OrthancException(ErrorCode_Plugin);
5503         }
5504       }
5505     }
5506 
5507     return true;
5508   }
5509 
5510 
UnserializeJob(const std::string & type,const Json::Value & value)5511   IJob* OrthancPlugins::UnserializeJob(const std::string& type,
5512                                        const Json::Value& value)
5513   {
5514     const std::string serialized = value.toStyledString();
5515 
5516     boost::mutex::scoped_lock lock(pimpl_->jobsUnserializersMutex_);
5517 
5518     for (PImpl::JobsUnserializers::iterator
5519            unserializer = pimpl_->jobsUnserializers_.begin();
5520          unserializer != pimpl_->jobsUnserializers_.end(); ++unserializer)
5521     {
5522       OrthancPluginJob* job = (*unserializer) (type.c_str(), serialized.c_str());
5523       if (job != NULL)
5524       {
5525         return reinterpret_cast<PluginsJob*>(job);
5526       }
5527     }
5528 
5529     return NULL;
5530   }
5531 
5532 
RefreshMetrics()5533   void OrthancPlugins::RefreshMetrics()
5534   {
5535     boost::mutex::scoped_lock lock(pimpl_->refreshMetricsMutex_);
5536 
5537     for (PImpl::RefreshMetricsCallbacks::iterator
5538            it = pimpl_->refreshMetricsCallbacks_.begin();
5539          it != pimpl_->refreshMetricsCallbacks_.end(); ++it)
5540     {
5541       if (*it != NULL)
5542       {
5543         (*it) ();
5544       }
5545     }
5546   }
5547 
5548 
5549   class OrthancPlugins::HttpServerChunkedReader : public IHttpHandler::IChunkedRequestReader
5550   {
5551   private:
5552     OrthancPluginServerChunkedRequestReader*  reader_;
5553     _OrthancPluginChunkedRestCallback         parameters_;
5554     PluginsErrorDictionary&                   errorDictionary_;
5555 
5556   public:
HttpServerChunkedReader(OrthancPluginServerChunkedRequestReader * reader,const _OrthancPluginChunkedRestCallback & parameters,PluginsErrorDictionary & errorDictionary)5557     HttpServerChunkedReader(OrthancPluginServerChunkedRequestReader* reader,
5558                             const _OrthancPluginChunkedRestCallback& parameters,
5559                             PluginsErrorDictionary& errorDictionary) :
5560       reader_(reader),
5561       parameters_(parameters),
5562       errorDictionary_(errorDictionary)
5563     {
5564       assert(reader_ != NULL);
5565     }
5566 
~HttpServerChunkedReader()5567     virtual ~HttpServerChunkedReader()
5568     {
5569       assert(reader_ != NULL);
5570       parameters_.finalize(reader_);
5571     }
5572 
AddBodyChunk(const void * data,size_t size)5573     virtual void AddBodyChunk(const void* data,
5574                               size_t size) ORTHANC_OVERRIDE
5575     {
5576       if (static_cast<uint32_t>(size) != size)
5577       {
5578         throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
5579       }
5580 
5581       assert(reader_ != NULL);
5582       parameters_.addChunk(reader_, data, size);
5583     }
5584 
Execute(HttpOutput & output)5585     virtual void Execute(HttpOutput& output) ORTHANC_OVERRIDE
5586     {
5587       assert(reader_ != NULL);
5588 
5589       PImpl::PluginHttpOutput pluginOutput(output);
5590 
5591       OrthancPluginErrorCode error = parameters_.execute(
5592         reader_, reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput));
5593 
5594       pluginOutput.Close(error, errorDictionary_);
5595     }
5596   };
5597 
5598 
CreateChunkedRequestReader(std::unique_ptr<IChunkedRequestReader> & target,RequestOrigin origin,const char * remoteIp,const char * username,HttpMethod method,const UriComponents & uri,const HttpToolbox::Arguments & headers)5599   bool OrthancPlugins::CreateChunkedRequestReader(std::unique_ptr<IChunkedRequestReader>& target,
5600                                                   RequestOrigin origin,
5601                                                   const char* remoteIp,
5602                                                   const char* username,
5603                                                   HttpMethod method,
5604                                                   const UriComponents& uri,
5605                                                   const HttpToolbox::Arguments& headers)
5606   {
5607     if (method != HttpMethod_Post &&
5608         method != HttpMethod_Put)
5609     {
5610       throw OrthancException(ErrorCode_InternalError);
5611     }
5612 
5613     RestCallbackMatcher matcher(uri);
5614 
5615     PImpl::ChunkedRestCallback* callback = NULL;
5616 
5617     // Loop over the callbacks registered by the plugins
5618     boost::shared_lock<boost::shared_mutex> lock(pimpl_->restCallbackRegistrationMutex_);
5619     for (PImpl::ChunkedRestCallbacks::const_iterator it = pimpl_->chunkedRestCallbacks_.begin();
5620          it != pimpl_->chunkedRestCallbacks_.end(); ++it)
5621     {
5622       if (matcher.IsMatch((*it)->GetRegularExpression()))
5623       {
5624         callback = *it;
5625         break;
5626       }
5627     }
5628 
5629     if (callback == NULL)
5630     {
5631       // Callback not found
5632       return false;
5633     }
5634     else
5635     {
5636       OrthancPluginServerChunkedRequestReaderFactory handler;
5637 
5638       switch (method)
5639       {
5640         case HttpMethod_Post:
5641           handler = callback->GetParameters().postHandler;
5642           break;
5643 
5644         case HttpMethod_Put:
5645           handler = callback->GetParameters().putHandler;
5646           break;
5647 
5648         default:
5649           handler = NULL;
5650           break;
5651       }
5652 
5653       if (handler == NULL)
5654       {
5655         return false;
5656       }
5657       else
5658       {
5659         CLOG(INFO, PLUGINS) << "Delegating chunked HTTP request to plugin for URI: " << matcher.GetFlatUri();
5660 
5661         HttpRequestConverter converter(matcher, method, headers);
5662         converter.GetRequest().body = NULL;
5663         converter.GetRequest().bodySize = 0;
5664 
5665         OrthancPluginServerChunkedRequestReader* reader = NULL;
5666 
5667         OrthancPluginErrorCode errorCode = handler(
5668           &reader, matcher.GetFlatUri().c_str(), &converter.GetRequest());
5669 
5670         if (errorCode != OrthancPluginErrorCode_Success)
5671         {
5672           throw OrthancException(static_cast<ErrorCode>(errorCode));
5673         }
5674         else if (reader == NULL)
5675         {
5676           // The plugin has not created a reader for chunked body
5677           return false;
5678         }
5679         else
5680         {
5681           target.reset(new HttpServerChunkedReader(reader, callback->GetParameters(), GetErrorDictionary()));
5682           return true;
5683         }
5684       }
5685     }
5686   }
5687 
5688 
CreateStorageCommitment(const std::string & jobId,const std::string & transactionUid,const std::vector<std::string> & sopClassUids,const std::vector<std::string> & sopInstanceUids,const std::string & remoteAet,const std::string & calledAet)5689   IStorageCommitmentFactory::ILookupHandler* OrthancPlugins::CreateStorageCommitment(
5690     const std::string& jobId,
5691     const std::string& transactionUid,
5692     const std::vector<std::string>& sopClassUids,
5693     const std::vector<std::string>& sopInstanceUids,
5694     const std::string& remoteAet,
5695     const std::string& calledAet)
5696   {
5697     boost::mutex::scoped_lock lock(pimpl_->storageCommitmentScpMutex_);
5698 
5699     for (PImpl::StorageCommitmentScpCallbacks::iterator
5700            it = pimpl_->storageCommitmentScpCallbacks_.begin();
5701          it != pimpl_->storageCommitmentScpCallbacks_.end(); ++it)
5702     {
5703       assert(*it != NULL);
5704       IStorageCommitmentFactory::ILookupHandler* handler = (*it)->CreateStorageCommitment
5705         (jobId, transactionUid, sopClassUids, sopInstanceUids, remoteAet, calledAet);
5706 
5707       if (handler != NULL)
5708       {
5709         return handler;
5710       }
5711     }
5712 
5713     return NULL;
5714   }
5715 
5716 
TranscodeBuffer(std::string & target,const void * buffer,size_t size,const std::set<DicomTransferSyntax> & allowedSyntaxes,bool allowNewSopInstanceUid)5717   bool OrthancPlugins::TranscodeBuffer(std::string& target,
5718                                        const void* buffer,
5719                                        size_t size,
5720                                        const std::set<DicomTransferSyntax>& allowedSyntaxes,
5721                                        bool allowNewSopInstanceUid)
5722   {
5723     boost::shared_lock<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
5724 
5725     if (pimpl_->transcoderCallbacks_.empty())
5726     {
5727       return false;
5728     }
5729 
5730     std::vector<const char*> uids;
5731     uids.reserve(allowedSyntaxes.size());
5732     for (std::set<DicomTransferSyntax>::const_iterator
5733            it = allowedSyntaxes.begin(); it != allowedSyntaxes.end(); ++it)
5734     {
5735       uids.push_back(GetTransferSyntaxUid(*it));
5736     }
5737 
5738     for (PImpl::TranscoderCallbacks::const_iterator
5739            transcoder = pimpl_->transcoderCallbacks_.begin();
5740          transcoder != pimpl_->transcoderCallbacks_.end(); ++transcoder)
5741     {
5742       MemoryBufferRaii a;
5743 
5744       if ((*transcoder) (a.GetObject(), buffer, size, uids.empty() ? NULL : &uids[0],
5745                          static_cast<uint32_t>(uids.size()), allowNewSopInstanceUid) ==
5746           OrthancPluginErrorCode_Success)
5747       {
5748         a.ToString(target);
5749         return true;
5750       }
5751     }
5752 
5753     return false;
5754   }
5755 
5756 
IsValidAuthorizationToken(const std::string & token) const5757   bool OrthancPlugins::IsValidAuthorizationToken(const std::string& token) const
5758   {
5759     boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
5760     return (pimpl_->authorizationTokens_.find(token) != pimpl_->authorizationTokens_.end());
5761   }
5762 
5763 
GetMaxDatabaseRetries() const5764   unsigned int OrthancPlugins::GetMaxDatabaseRetries() const
5765   {
5766     boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
5767     return pimpl_->maxDatabaseRetries_;
5768   }
5769 }
5770