Mercurial > hg > orthanc
comparison OrthancServer/Sources/main.cpp @ 4230:b313a0001893
WebDavStorage
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 06 Oct 2020 18:14:26 +0200 |
parents | c8c0bbaaace3 |
children | 290ffcb0a147 |
comparison
equal
deleted
inserted
replaced
4229:013d6c6b2387 | 4230:b313a0001893 |
---|---|
41 #include "../../OrthancFramework/Sources/DicomNetworking/DicomAssociationParameters.h" | 41 #include "../../OrthancFramework/Sources/DicomNetworking/DicomAssociationParameters.h" |
42 #include "../../OrthancFramework/Sources/DicomNetworking/DicomServer.h" | 42 #include "../../OrthancFramework/Sources/DicomNetworking/DicomServer.h" |
43 #include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" | 43 #include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" |
44 #include "../../OrthancFramework/Sources/HttpServer/FilesystemHttpHandler.h" | 44 #include "../../OrthancFramework/Sources/HttpServer/FilesystemHttpHandler.h" |
45 #include "../../OrthancFramework/Sources/HttpServer/HttpServer.h" | 45 #include "../../OrthancFramework/Sources/HttpServer/HttpServer.h" |
46 #include "../../OrthancFramework/Sources/HttpServer/IWebDavBucket.h" // TODO | |
47 #include "../../OrthancFramework/Sources/Logging.h" | 46 #include "../../OrthancFramework/Sources/Logging.h" |
48 #include "../../OrthancFramework/Sources/Lua/LuaFunctionCall.h" | 47 #include "../../OrthancFramework/Sources/Lua/LuaFunctionCall.h" |
49 #include "../Plugins/Engine/OrthancPlugins.h" | 48 #include "../Plugins/Engine/OrthancPlugins.h" |
50 #include "EmbeddedResourceHttpHandler.h" | 49 #include "EmbeddedResourceHttpHandler.h" |
51 #include "OrthancConfiguration.h" | 50 #include "OrthancConfiguration.h" |
56 #include "ServerContext.h" | 55 #include "ServerContext.h" |
57 #include "ServerJobs/StorageCommitmentScpJob.h" | 56 #include "ServerJobs/StorageCommitmentScpJob.h" |
58 #include "ServerToolbox.h" | 57 #include "ServerToolbox.h" |
59 #include "StorageCommitmentReports.h" | 58 #include "StorageCommitmentReports.h" |
60 | 59 |
60 #include "../../OrthancFramework/Sources/HttpServer/WebDavStorage.h" // TODO | |
61 | |
62 | |
61 using namespace Orthanc; | 63 using namespace Orthanc; |
62 | 64 |
63 | 65 |
64 class OrthancStoreRequestHandler : public IStoreRequestHandler | 66 class OrthancStoreRequestHandler : public IStoreRequestHandler |
65 { | 67 { |
611 }; | 613 }; |
612 | 614 |
613 | 615 |
614 | 616 |
615 | 617 |
618 | |
619 | |
620 | |
616 static const char* const UPLOAD_FOLDER = "upload"; | 621 static const char* const UPLOAD_FOLDER = "upload"; |
617 | 622 |
618 class DummyBucket : public IWebDavBucket // TODO | 623 class DummyBucket : public IWebDavBucket // TODO |
619 { | 624 { |
620 private: | 625 private: |
621 ServerContext& context_; | 626 ServerContext& context_; |
622 | 627 WebDavStorage storage_; |
623 static void RemoveFirstElement(std::vector<std::string>& target, | |
624 const std::vector<std::string>& source) | |
625 { | |
626 if (source.empty()) | |
627 { | |
628 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
629 } | |
630 else | |
631 { | |
632 target.resize(source.size() - 1); | |
633 for (size_t i = 1; i < source.size(); i++) | |
634 { | |
635 target[i - 1] = source[i]; | |
636 } | |
637 } | |
638 } | |
639 | |
640 class UploadedFile : public boost::noncopyable | |
641 { | |
642 private: | |
643 std::string content_; | |
644 MimeType mime_; | |
645 boost::posix_time::ptime time_; | |
646 | |
647 void Touch() | |
648 { | |
649 time_ = boost::posix_time::second_clock::universal_time(); | |
650 } | |
651 | |
652 public: | |
653 UploadedFile() : | |
654 mime_(MimeType_Binary) | |
655 { | |
656 Touch(); | |
657 } | |
658 | |
659 void SetContent(const std::string& content, | |
660 MimeType mime) | |
661 { | |
662 content_ = content; | |
663 mime_ = mime; | |
664 Touch(); | |
665 } | |
666 | |
667 MimeType GetMimeType() const | |
668 { | |
669 return mime_; | |
670 } | |
671 | |
672 const std::string& GetContent() const | |
673 { | |
674 return content_; | |
675 } | |
676 | |
677 const boost::posix_time::ptime& GetTime() const | |
678 { | |
679 return time_; | |
680 } | |
681 }; | |
682 | |
683 | |
684 class UploadedFolder : public boost::noncopyable | |
685 { | |
686 private: | |
687 typedef std::map<std::string, UploadedFile*> Files; | |
688 typedef std::map<std::string, UploadedFolder*> Subfolders; | |
689 | |
690 Files files_; | |
691 Subfolders subfolders_; | |
692 | |
693 void CheckName(const std::string& name) | |
694 { | |
695 if (name.empty() || | |
696 name.find('/') != std::string::npos || | |
697 name.find('\\') != std::string::npos || | |
698 name.find('\0') != std::string::npos) | |
699 { | |
700 throw OrthancException(ErrorCode_ParameterOutOfRange, | |
701 "Bad resource name for WebDAV: " + name); | |
702 } | |
703 } | |
704 | |
705 bool IsExisting(const std::string& name) const | |
706 { | |
707 return (files_.find(name) != files_.end() || | |
708 subfolders_.find(name) != subfolders_.end()); | |
709 } | |
710 | |
711 public: | |
712 ~UploadedFolder() | |
713 { | |
714 for (Files::iterator it = files_.begin(); it != files_.end(); ++it) | |
715 { | |
716 assert(it->second != NULL); | |
717 delete it->second; | |
718 } | |
719 | |
720 for (Subfolders::iterator it = subfolders_.begin(); it != subfolders_.end(); ++it) | |
721 { | |
722 assert(it->second != NULL); | |
723 delete it->second; | |
724 } | |
725 } | |
726 | |
727 const UploadedFile* LookupFile(const std::string& name) const | |
728 { | |
729 Files::const_iterator found = files_.find(name); | |
730 if (found == files_.end()) | |
731 { | |
732 return NULL; | |
733 } | |
734 else | |
735 { | |
736 assert(found->second != NULL); | |
737 return found->second; | |
738 } | |
739 } | |
740 | |
741 bool CreateSubfolder(const std::string& name) | |
742 { | |
743 CheckName(name); | |
744 | |
745 if (IsExisting(name)) | |
746 { | |
747 LOG(ERROR) << "WebDAV folder already existing: " << name; | |
748 return false; | |
749 } | |
750 else | |
751 { | |
752 subfolders_[name] = new UploadedFolder; | |
753 return true; | |
754 } | |
755 } | |
756 | |
757 bool StoreFile(const std::string& name, | |
758 const std::string& content, | |
759 MimeType mime) | |
760 { | |
761 CheckName(name); | |
762 | |
763 if (subfolders_.find(name) != subfolders_.end()) | |
764 { | |
765 LOG(ERROR) << "WebDAV folder already existing: " << name; | |
766 return false; | |
767 } | |
768 | |
769 Files::iterator found = files_.find(name); | |
770 if (found == files_.end()) | |
771 { | |
772 std::unique_ptr<UploadedFile> f(new UploadedFile); | |
773 f->SetContent(content, mime); | |
774 files_[name] = f.release(); | |
775 } | |
776 else | |
777 { | |
778 assert(found->second != NULL); | |
779 found->second->SetContent(content, mime); | |
780 } | |
781 | |
782 return true; | |
783 } | |
784 | |
785 UploadedFolder* LookupSubfolder(const UriComponents& path) | |
786 { | |
787 if (path.empty()) | |
788 { | |
789 return this; | |
790 } | |
791 else | |
792 { | |
793 Subfolders::const_iterator found = subfolders_.find(path[0]); | |
794 if (found == subfolders_.end()) | |
795 { | |
796 return NULL; | |
797 } | |
798 else | |
799 { | |
800 assert(found->second != NULL); | |
801 | |
802 UriComponents p; | |
803 RemoveFirstElement(p, path); | |
804 | |
805 return found->second->LookupSubfolder(p); | |
806 } | |
807 } | |
808 } | |
809 | |
810 void ListCollection(Collection& collection) const | |
811 { | |
812 for (Files::const_iterator it = files_.begin(); it != files_.end(); ++it) | |
813 { | |
814 assert(it->second != NULL); | |
815 | |
816 std::unique_ptr<File> f(new File(it->first)); | |
817 f->SetContentLength(it->second->GetContent().size()); | |
818 f->SetCreationTime(it->second->GetTime()); | |
819 collection.AddResource(f.release()); | |
820 } | |
821 | |
822 for (Subfolders::const_iterator it = subfolders_.begin(); it != subfolders_.end(); ++it) | |
823 { | |
824 collection.AddResource(new Folder(it->first)); | |
825 } | |
826 } | |
827 }; | |
828 | 628 |
829 bool IsUploadedFolder(const UriComponents& path) const | 629 bool IsUploadedFolder(const UriComponents& path) const |
830 { | 630 { |
831 return (path.size() >= 1 && | 631 return (path.size() >= 1 && path[0] == UPLOAD_FOLDER); |
832 path[0] == UPLOAD_FOLDER); | 632 } |
833 } | 633 |
834 | |
835 UploadedFolder uploads_; | |
836 boost::recursive_mutex mutex_; | |
837 | |
838 UploadedFolder* LookupUploadedFolder(const UriComponents& path) | |
839 { | |
840 if (IsUploadedFolder(path)) | |
841 { | |
842 UriComponents p; | |
843 RemoveFirstElement(p, path); | |
844 | |
845 return uploads_.LookupSubfolder(p); | |
846 } | |
847 else | |
848 { | |
849 return NULL; | |
850 } | |
851 } | |
852 | |
853 public: | 634 public: |
854 DummyBucket(ServerContext& context) : | 635 DummyBucket(ServerContext& context, |
855 context_(context) | 636 bool isMemory) : |
637 context_(context), | |
638 storage_(isMemory) | |
856 { | 639 { |
857 } | 640 } |
858 | 641 |
859 virtual bool IsExistingFolder(const UriComponents& path) ORTHANC_OVERRIDE | 642 virtual bool IsExistingFolder(const UriComponents& path) ORTHANC_OVERRIDE |
860 { | 643 { |
861 boost::recursive_mutex::scoped_lock lock(mutex_); | |
862 | |
863 if (IsUploadedFolder(path)) | 644 if (IsUploadedFolder(path)) |
864 { | 645 { |
865 return LookupUploadedFolder(path) != NULL; | 646 return storage_.IsExistingFolder(UriComponents(path.begin() + 1, path.end())); |
866 } | 647 } |
867 else | 648 else |
868 { | 649 { |
869 return (path.size() == 0 || | 650 return (path.size() == 0 || |
870 (path.size() == 1 && path[0] == "Folder1") || | 651 (path.size() == 1 && path[0] == "Folder1") || |
873 } | 654 } |
874 | 655 |
875 virtual bool ListCollection(Collection& collection, | 656 virtual bool ListCollection(Collection& collection, |
876 const UriComponents& path) ORTHANC_OVERRIDE | 657 const UriComponents& path) ORTHANC_OVERRIDE |
877 { | 658 { |
878 boost::recursive_mutex::scoped_lock lock(mutex_); | |
879 | |
880 if (IsUploadedFolder(path)) | 659 if (IsUploadedFolder(path)) |
881 { | 660 { |
882 const UploadedFolder* folder = LookupUploadedFolder(path); | 661 return storage_.ListCollection(collection, UriComponents(path.begin() + 1, path.end())); |
883 if (folder == NULL) | |
884 { | |
885 return false; | |
886 } | |
887 else | |
888 { | |
889 folder->ListCollection(collection); | |
890 return true; | |
891 } | |
892 } | 662 } |
893 else if (IsExistingFolder(path)) | 663 else if (IsExistingFolder(path)) |
894 { | 664 { |
895 if (path.empty()) | 665 if (path.empty()) |
896 { | 666 { |
921 virtual bool GetFileContent(MimeType& mime, | 691 virtual bool GetFileContent(MimeType& mime, |
922 std::string& content, | 692 std::string& content, |
923 boost::posix_time::ptime& modificationTime, | 693 boost::posix_time::ptime& modificationTime, |
924 const UriComponents& path) ORTHANC_OVERRIDE | 694 const UriComponents& path) ORTHANC_OVERRIDE |
925 { | 695 { |
926 boost::recursive_mutex::scoped_lock lock(mutex_); | |
927 | |
928 if (path.empty()) | 696 if (path.empty()) |
929 { | 697 { |
930 return false; | 698 return false; |
931 } | 699 } |
932 else if (IsUploadedFolder(path)) | 700 else if (IsUploadedFolder(path)) |
933 { | 701 { |
934 std::vector<std::string> p(path.begin(), path.end() - 1); | 702 return storage_.GetFileContent(mime, content, modificationTime, |
935 | 703 UriComponents(path.begin() + 1, path.end())); |
936 const UploadedFolder* folder = LookupUploadedFolder(p); | |
937 if (folder == NULL) | |
938 { | |
939 return false; | |
940 } | |
941 | |
942 const UploadedFile* file = folder->LookupFile(path.back()); | |
943 if (file == NULL) | |
944 { | |
945 return false; | |
946 } | |
947 else | |
948 { | |
949 mime = file->GetMimeType(); | |
950 content = file->GetContent(); | |
951 modificationTime = file->GetTime(); | |
952 return true; | |
953 } | |
954 } | 704 } |
955 else if (path.back() == "IM0.dcm" || | 705 else if (path.back() == "IM0.dcm" || |
956 path.back() == "IM1.dcm" || | 706 path.back() == "IM1.dcm" || |
957 path.back() == "IM2.dcm" || | 707 path.back() == "IM2.dcm" || |
958 path.back() == "IM3.dcm" || | 708 path.back() == "IM3.dcm" || |
978 | 728 |
979 | 729 |
980 virtual bool StoreFile(const std::string& content, | 730 virtual bool StoreFile(const std::string& content, |
981 const UriComponents& path) ORTHANC_OVERRIDE | 731 const UriComponents& path) ORTHANC_OVERRIDE |
982 { | 732 { |
983 boost::recursive_mutex::scoped_lock lock(mutex_); | |
984 | |
985 if (IsUploadedFolder(path)) | 733 if (IsUploadedFolder(path)) |
986 { | 734 { |
987 std::vector<std::string> p(path.begin(), path.end() - 1); | 735 return storage_.StoreFile(content, UriComponents(path.begin() + 1, path.end())); |
988 | |
989 UploadedFolder* folder = LookupUploadedFolder(p); | |
990 if (folder == NULL) | |
991 { | |
992 return false; | |
993 } | |
994 else | |
995 { | |
996 printf("STORING %d bytes at %s\n", content.size(), path.back().c_str()); | |
997 return folder->StoreFile(path.back(), content, SystemToolbox::AutodetectMimeType(path.back())); | |
998 } | |
999 } | 736 } |
1000 else | 737 else |
1001 { | 738 { |
1002 LOG(WARNING) << "Writing to a read-only location in WebDAV: " << Toolbox::FlattenUri(path); | 739 LOG(WARNING) << "Writing to a read-only location in WebDAV: " << Toolbox::FlattenUri(path); |
1003 return false; | 740 return false; |
1005 } | 742 } |
1006 | 743 |
1007 | 744 |
1008 virtual bool CreateFolder(const UriComponents& path) | 745 virtual bool CreateFolder(const UriComponents& path) |
1009 { | 746 { |
1010 boost::recursive_mutex::scoped_lock lock(mutex_); | |
1011 | |
1012 if (IsUploadedFolder(path)) | 747 if (IsUploadedFolder(path)) |
1013 { | 748 { |
1014 std::vector<std::string> p(path.begin(), path.end() - 1); | 749 return storage_.CreateFolder(UriComponents(path.begin() + 1, path.end())); |
1015 | |
1016 UploadedFolder* folder = LookupUploadedFolder(p); | |
1017 if (folder == NULL) | |
1018 { | |
1019 return false; | |
1020 } | |
1021 else | |
1022 { | |
1023 printf("CREATING FOLDER %s\n", path.back().c_str()); | |
1024 return folder->CreateSubfolder(path.back()); | |
1025 } | |
1026 } | 750 } |
1027 else | 751 else |
1028 { | 752 { |
1029 LOG(WARNING) << "Writing to a read-only location in WebDAV: " << Toolbox::FlattenUri(path); | 753 LOG(WARNING) << "Writing to a read-only location in WebDAV: " << Toolbox::FlattenUri(path); |
1030 return false; | 754 return false; |
1031 } | 755 } |
1032 } | 756 } |
1033 | 757 |
1034 | |
1035 virtual void Start() ORTHANC_OVERRIDE | 758 virtual void Start() ORTHANC_OVERRIDE |
1036 { | 759 { |
1037 boost::recursive_mutex::scoped_lock lock(mutex_); | |
1038 | |
1039 LOG(WARNING) << "Starting WebDAV"; | 760 LOG(WARNING) << "Starting WebDAV"; |
1040 } | 761 } |
1041 | 762 |
1042 | |
1043 virtual void Stop() ORTHANC_OVERRIDE | 763 virtual void Stop() ORTHANC_OVERRIDE |
1044 { | 764 { |
1045 boost::recursive_mutex::scoped_lock lock(mutex_); | |
1046 | |
1047 LOG(WARNING) << "Stopping WebDAV"; | 765 LOG(WARNING) << "Stopping WebDAV"; |
1048 } | 766 } |
1049 }; | 767 }; |
1050 | 768 |
1051 | 769 |
1489 | 1207 |
1490 { | 1208 { |
1491 UriComponents root; // TODO | 1209 UriComponents root; // TODO |
1492 root.push_back("a"); | 1210 root.push_back("a"); |
1493 root.push_back("b"); | 1211 root.push_back("b"); |
1494 httpServer.Register(root, new DummyBucket(context)); | 1212 //httpServer.Register(root, new WebDavStorage(true)); |
1213 httpServer.Register(root, new DummyBucket(context, true)); | |
1495 } | 1214 } |
1496 | 1215 |
1497 if (httpServer.GetPortNumber() < 1024) | 1216 if (httpServer.GetPortNumber() < 1024) |
1498 { | 1217 { |
1499 LOG(WARNING) << "The HTTP port is privileged (" | 1218 LOG(WARNING) << "The HTTP port is privileged (" |