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