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