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 ("