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 #pragma once
35 
36 #include "IServerListener.h"
37 #include "LuaScripting.h"
38 #include "OrthancHttpHandler.h"
39 #include "ServerIndex.h"
40 #include "ServerJobs/IStorageCommitmentFactory.h"
41 
42 #include "../../OrthancFramework/Sources/DicomFormat/DicomElement.h"
43 #include "../../OrthancFramework/Sources/DicomParsing/DicomModification.h"
44 #include "../../OrthancFramework/Sources/DicomParsing/IDicomTranscoder.h"
45 #include "../../OrthancFramework/Sources/DicomParsing/ParsedDicomCache.h"
46 #include "../../OrthancFramework/Sources/MultiThreading/Semaphore.h"
47 
48 
49 namespace Orthanc
50 {
51   class DicomInstanceToStore;
52   class IStorageArea;
53   class JobsEngine;
54   class MetricsRegistry;
55   class OrthancPlugins;
56   class ParsedDicomFile;
57   class RestApiOutput;
58   class SetOfInstancesJob;
59   class SharedArchive;
60   class SharedMessageQueue;
61   class StorageCommitmentReports;
62 
63 
64   /**
65    * This class is responsible for maintaining the storage area on the
66    * filesystem (including compression), as well as the index of the
67    * DICOM store. It implements the required locking mechanisms.
68    **/
69   class ServerContext :
70     public IStorageCommitmentFactory,
71     public IDicomTranscoder,
72     private JobsRegistry::IObserver
73   {
74     friend class ServerIndex;  // To access "RemoveFile()"
75 
76   public:
77     class ILookupVisitor : public boost::noncopyable
78     {
79     public:
~ILookupVisitor()80       virtual ~ILookupVisitor()
81       {
82       }
83 
84       virtual bool IsDicomAsJsonNeeded() const = 0;
85 
86       virtual void MarkAsComplete() = 0;
87 
88       // NB: "dicomAsJson" must *not* be deleted, and can be NULL if
89       // "!IsDicomAsJsonNeeded()"
90       virtual void Visit(const std::string& publicId,
91                          const std::string& instanceId,
92                          const DicomMap& mainDicomTags,
93                          const Json::Value* dicomAsJson) = 0;
94     };
95 
96 
97   private:
98     class LuaServerListener : public IServerListener
99     {
100     private:
101       ServerContext& context_;
102 
103     public:
LuaServerListener(ServerContext & context)104       explicit LuaServerListener(ServerContext& context) :
105         context_(context)
106       {
107       }
108 
SignalStoredInstance(const std::string & publicId,const DicomInstanceToStore & instance,const Json::Value & simplifiedTags)109       virtual void SignalStoredInstance(const std::string& publicId,
110                                         const DicomInstanceToStore& instance,
111                                         const Json::Value& simplifiedTags) ORTHANC_OVERRIDE
112       {
113         context_.mainLua_.SignalStoredInstance(publicId, instance, simplifiedTags);
114       }
115 
SignalChange(const ServerIndexChange & change)116       virtual void SignalChange(const ServerIndexChange& change) ORTHANC_OVERRIDE
117       {
118         context_.mainLua_.SignalChange(change);
119       }
120 
FilterIncomingInstance(const DicomInstanceToStore & instance,const Json::Value & simplified)121       virtual bool FilterIncomingInstance(const DicomInstanceToStore& instance,
122                                           const Json::Value& simplified) ORTHANC_OVERRIDE
123       {
124         return context_.filterLua_.FilterIncomingInstance(instance, simplified);
125       }
126     };
127 
128     class ServerListener
129     {
130     private:
131       IServerListener *listener_;
132       std::string      description_;
133 
134     public:
ServerListener(IServerListener & listener,const std::string & description)135       ServerListener(IServerListener& listener,
136                      const std::string& description) :
137         listener_(&listener),
138         description_(description)
139       {
140       }
141 
GetListener()142       IServerListener& GetListener()
143       {
144         return *listener_;
145       }
146 
GetDescription()147       const std::string& GetDescription()
148       {
149         return description_;
150       }
151     };
152 
153     typedef std::list<ServerListener>  ServerListeners;
154 
155 
156     static void ChangeThread(ServerContext* that,
157                              unsigned int sleepDelay);
158 
159     static void SaveJobsThread(ServerContext* that,
160                                unsigned int sleepDelay);
161 
162     void SaveJobsEngine();
163 
164     virtual void SignalJobSubmitted(const std::string& jobId) ORTHANC_OVERRIDE;
165 
166     virtual void SignalJobSuccess(const std::string& jobId) ORTHANC_OVERRIDE;
167 
168     virtual void SignalJobFailure(const std::string& jobId) ORTHANC_OVERRIDE;
169 
170     ServerIndex index_;
171     IStorageArea& area_;
172 
173     bool compressionEnabled_;
174     bool storeMD5_;
175 
176     Semaphore largeDicomThrottler_;  // New in Orthanc 1.9.0 (notably for very large DICOM files in WSI)
177     ParsedDicomCache  dicomCache_;
178 
179     LuaScripting mainLua_;
180     LuaScripting filterLua_;
181     LuaServerListener  luaListener_;
182     std::unique_ptr<SharedArchive>  mediaArchive_;
183 
184     // The "JobsEngine" must be *after* "LuaScripting", as
185     // "LuaScripting" embeds "LuaJobManager" that registers as an
186     // observer to "SequenceOfOperationsJob", whose lifetime
187     // corresponds to that of "JobsEngine". It must also be after
188     // "mediaArchive_", as jobs might access this archive.
189     JobsEngine jobsEngine_;
190 
191 #if ORTHANC_ENABLE_PLUGINS == 1
192     OrthancPlugins* plugins_;
193 #endif
194 
195     ServerListeners listeners_;
196     boost::shared_mutex listenersMutex_;
197 
198     bool done_;
199     bool haveJobsChanged_;
200     bool isJobsEngineUnserialized_;
201     SharedMessageQueue  pendingChanges_;
202     boost::thread  changeThread_;
203     boost::thread  saveJobsThread_;
204 
205     std::unique_ptr<SharedArchive>  queryRetrieveArchive_;
206     std::string defaultLocalAet_;
207     OrthancHttpHandler  httpHandler_;
208     bool saveJobs_;
209     FindStorageAccessMode findStorageAccessMode_;
210     unsigned int limitFindInstances_;
211     unsigned int limitFindResults_;
212 
213     std::unique_ptr<MetricsRegistry>  metricsRegistry_;
214     bool isHttpServerSecure_;
215     bool isExecuteLuaEnabled_;
216     bool overwriteInstances_;
217 
218     std::unique_ptr<StorageCommitmentReports>  storageCommitmentReports_;
219 
220     bool transcodeDicomProtocol_;
221     std::unique_ptr<IDicomTranscoder>  dcmtkTranscoder_;
222     BuiltinDecoderTranscoderOrder builtinDecoderTranscoderOrder_;
223     bool isIngestTranscoding_;
224     DicomTransferSyntax ingestTransferSyntax_;
225     bool ingestTranscodingOfUncompressed_;
226     bool ingestTranscodingOfCompressed_;
227 
228     // New in Orthanc 1.9.0
229     DicomTransferSyntax preferredTransferSyntax_;
230     boost::mutex dynamicOptionsMutex_;
231     bool isUnknownSopClassAccepted_;
232     std::set<DicomTransferSyntax>  acceptedTransferSyntaxes_;
233 
234     StoreStatus StoreAfterTranscoding(std::string& resultPublicId,
235                                       DicomInstanceToStore& dicom,
236                                       StoreInstanceMode mode);
237 
238     void ApplyInternal(ILookupVisitor& visitor,
239                        const DatabaseLookup& lookup,
240                        ResourceType queryLevel,
241                        size_t since,
242                        size_t limit);
243 
244     void PublishDicomCacheMetrics();
245 
246     // This method must only be called from "ServerIndex"!
247     void RemoveFile(const std::string& fileUuid,
248                     FileContentType type);
249 
250     // This DicomModification object is intended to be used as a
251     // "rules engine" when de-identifying logs for C-Find, C-Get, and
252     // C-Move queries (new in Orthanc 1.8.2)
253     DicomModification logsDeidentifierRules_;
254     bool              deidentifyLogs_;
255 
256   public:
257     class DicomCacheLocker : public boost::noncopyable
258     {
259     private:
260       ServerContext&                               context_;
261       std::string                                  instancePublicId_;
262       std::unique_ptr<ParsedDicomCache::Accessor>  accessor_;
263       std::unique_ptr<ParsedDicomFile>             dicom_;
264       size_t                                       dicomSize_;
265       std::unique_ptr<Semaphore::Locker>           largeDicomLocker_;
266 
267     public:
268       DicomCacheLocker(ServerContext& context,
269                        const std::string& instancePublicId);
270 
271       ~DicomCacheLocker();
272 
273       ParsedDicomFile& GetDicom() const;
274     };
275 
276     ServerContext(IDatabaseWrapper& database,
277                   IStorageArea& area,
278                   bool unitTesting,
279                   size_t maxCompletedJobs);
280 
281     ~ServerContext();
282 
283     void SetupJobsEngine(bool unitTesting,
284                          bool loadJobsFromDatabase);
285 
GetIndex()286     ServerIndex& GetIndex()
287     {
288       return index_;
289     }
290 
291     void SetCompressionEnabled(bool enabled);
292 
IsCompressionEnabled()293     bool IsCompressionEnabled() const
294     {
295       return compressionEnabled_;
296     }
297 
298     bool AddAttachment(int64_t& newRevision,
299                        const std::string& resourceId,
300                        FileContentType attachmentType,
301                        const void* data,
302                        size_t size,
303                        bool hasOldRevision,
304                        int64_t oldRevision,
305                        const std::string& oldMD5);
306 
307     StoreStatus Store(std::string& resultPublicId,
308                       DicomInstanceToStore& dicom,
309                       StoreInstanceMode mode);
310 
311     void AnswerAttachment(RestApiOutput& output,
312                           const std::string& resourceId,
313                           FileContentType content);
314 
315     void ChangeAttachmentCompression(const std::string& resourceId,
316                                      FileContentType attachmentType,
317                                      CompressionType compression);
318 
319     void ReadDicomAsJson(Json::Value& result,
320                          const std::string& instancePublicId,
321                          const std::set<DicomTag>& ignoreTagLength);
322 
323     void ReadDicomAsJson(Json::Value& result,
324                          const std::string& instancePublicId);
325 
326     void ReadDicom(std::string& dicom,
327                    const std::string& instancePublicId);
328 
329     bool ReadDicomUntilPixelData(std::string& dicom,
330                                  const std::string& instancePublicId);
331 
332     // This method is for low-level operations on "/instances/.../attachments/..."
333     void ReadAttachment(std::string& result,
334                         int64_t& revision,
335                         const std::string& instancePublicId,
336                         FileContentType content,
337                         bool uncompressIfNeeded);
338 
339     void SetStoreMD5ForAttachments(bool storeMD5);
340 
IsStoreMD5ForAttachments()341     bool IsStoreMD5ForAttachments() const
342     {
343       return storeMD5_;
344     }
345 
GetJobsEngine()346     JobsEngine& GetJobsEngine()
347     {
348       return jobsEngine_;
349     }
350 
351     bool DeleteResource(Json::Value& target,
352                         const std::string& uuid,
353                         ResourceType expectedType);
354 
355     void SignalChange(const ServerIndexChange& change);
356 
GetQueryRetrieveArchive()357     SharedArchive& GetQueryRetrieveArchive()
358     {
359       return *queryRetrieveArchive_;
360     }
361 
GetMediaArchive()362     SharedArchive& GetMediaArchive()
363     {
364       return *mediaArchive_;
365     }
366 
GetDefaultLocalApplicationEntityTitle()367     const std::string& GetDefaultLocalApplicationEntityTitle() const
368     {
369       return defaultLocalAet_;
370     }
371 
GetLuaScripting()372     LuaScripting& GetLuaScripting()
373     {
374       return mainLua_;
375     }
376 
GetHttpHandler()377     OrthancHttpHandler& GetHttpHandler()
378     {
379       return httpHandler_;
380     }
381 
382     void Stop();
383 
384     void Apply(ILookupVisitor& visitor,
385                const DatabaseLookup& lookup,
386                ResourceType queryLevel,
387                size_t since,
388                size_t limit);
389 
390     bool LookupOrReconstructMetadata(std::string& target,
391                                      const std::string& publicId,
392                                      ResourceType level,
393                                      MetadataType type);
394 
395 
396     /**
397      * Management of the plugins
398      **/
399 
400 #if ORTHANC_ENABLE_PLUGINS == 1
401     void SetPlugins(OrthancPlugins& plugins);
402 
403     void ResetPlugins();
404 
405     const OrthancPlugins& GetPlugins() const;
406 
407     OrthancPlugins& GetPlugins();
408 #endif
409 
410     bool HasPlugins() const;
411 
412     void AddChildInstances(SetOfInstancesJob& job,
413                            const std::string& publicId);
414 
415     void SignalUpdatedModalities();
416 
417     void SignalUpdatedPeers();
418 
GetMetricsRegistry()419     MetricsRegistry& GetMetricsRegistry()
420     {
421       return *metricsRegistry_;
422     }
423 
SetHttpServerSecure(bool isSecure)424     void SetHttpServerSecure(bool isSecure)
425     {
426       isHttpServerSecure_ = isSecure;
427     }
428 
IsHttpServerSecure()429     bool IsHttpServerSecure() const
430     {
431       return isHttpServerSecure_;
432     }
433 
SetExecuteLuaEnabled(bool enabled)434     void SetExecuteLuaEnabled(bool enabled)
435     {
436       isExecuteLuaEnabled_ = enabled;
437     }
438 
IsExecuteLuaEnabled()439     bool IsExecuteLuaEnabled() const
440     {
441       return isExecuteLuaEnabled_;
442     }
443 
SetOverwriteInstances(bool overwrite)444     void SetOverwriteInstances(bool overwrite)
445     {
446       overwriteInstances_ = overwrite;
447     }
448 
IsOverwriteInstances()449     bool IsOverwriteInstances() const
450     {
451       return overwriteInstances_;
452     }
453 
454     virtual IStorageCommitmentFactory::ILookupHandler*
455     CreateStorageCommitment(const std::string& jobId,
456                             const std::string& transactionUid,
457                             const std::vector<std::string>& sopClassUids,
458                             const std::vector<std::string>& sopInstanceUids,
459                             const std::string& remoteAet,
460                             const std::string& calledAet) ORTHANC_OVERRIDE;
461 
GetStorageCommitmentReports()462     StorageCommitmentReports& GetStorageCommitmentReports()
463     {
464       return *storageCommitmentReports_;
465     }
466 
467     ImageAccessor* DecodeDicomFrame(const std::string& publicId,
468                                     unsigned int frameIndex);
469 
470     ImageAccessor* DecodeDicomFrame(const DicomInstanceToStore& dicom,
471                                     unsigned int frameIndex);
472 
473     ImageAccessor* DecodeDicomFrame(const void* dicom,
474                                     size_t size,
475                                     unsigned int frameIndex);
476 
477     void StoreWithTranscoding(std::string& sopClassUid,
478                               std::string& sopInstanceUid,
479                               DicomStoreUserConnection& connection,
480                               const std::string& dicom,
481                               bool hasMoveOriginator,
482                               const std::string& moveOriginatorAet,
483                               uint16_t moveOriginatorId);
484 
485     // This method can be used even if the global option
486     // "TranscodeDicomProtocol" is set to "false"
487     virtual bool Transcode(DicomImage& target,
488                            DicomImage& source /* in, "GetParsed()" possibly modified */,
489                            const std::set<DicomTransferSyntax>& allowedSyntaxes,
490                            bool allowNewSopInstanceUid) ORTHANC_OVERRIDE;
491 
IsTranscodeDicomProtocol()492     bool IsTranscodeDicomProtocol() const
493     {
494       return transcodeDicomProtocol_;
495     }
496 
497     const std::string& GetDeidentifiedContent(const DicomElement& element) const;
498 
499     void GetAcceptedTransferSyntaxes(std::set<DicomTransferSyntax>& syntaxes);
500 
501     void SetAcceptedTransferSyntaxes(const std::set<DicomTransferSyntax>& syntaxes);
502 
503     bool IsUnknownSopClassAccepted();
504 
505     void SetUnknownSopClassAccepted(bool accepted);
506   };
507 }
508