comparison OrthancServer/Sources/main.cpp @ 4228:c8c0bbaaace3

write access to webdav
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 06 Oct 2020 12:45:11 +0200
parents 7fff7e683d65
children b313a0001893
comparison
equal deleted inserted replaced
4227:7fff7e683d65 4228:c8c0bbaaace3
611 }; 611 };
612 612
613 613
614 614
615 615
616 static const char* const UPLOAD_FOLDER = "upload";
617
616 class DummyBucket : public IWebDavBucket // TODO 618 class DummyBucket : public IWebDavBucket // TODO
617 { 619 {
618 private: 620 private:
619 ServerContext& context_; 621 ServerContext& context_;
620 622
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
829 bool IsUploadedFolder(const UriComponents& path) const
830 {
831 return (path.size() >= 1 &&
832 path[0] == UPLOAD_FOLDER);
833 }
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
621 public: 853 public:
622 DummyBucket(ServerContext& context) : 854 DummyBucket(ServerContext& context) :
623 context_(context) 855 context_(context)
624 { 856 {
625 } 857 }
626 858
627 virtual bool IsExistingFolder(const std::vector<std::string>& path) ORTHANC_OVERRIDE 859 virtual bool IsExistingFolder(const UriComponents& path) ORTHANC_OVERRIDE
628 { 860 {
629 return (path.size() == 0 || 861 boost::recursive_mutex::scoped_lock lock(mutex_);
630 (path.size() == 1 && path[0] == "Folder1") || 862
631 (path.size() == 2 && path[0] == "Folder1" && path[1] == "Folder2")); 863 if (IsUploadedFolder(path))
864 {
865 return LookupUploadedFolder(path) != NULL;
866 }
867 else
868 {
869 return (path.size() == 0 ||
870 (path.size() == 1 && path[0] == "Folder1") ||
871 (path.size() == 2 && path[0] == "Folder1" && path[1] == "Folder2"));
872 }
632 } 873 }
633 874
634 virtual bool ListCollection(Collection& collection, 875 virtual bool ListCollection(Collection& collection,
635 const UriComponents& path) ORTHANC_OVERRIDE 876 const UriComponents& path) ORTHANC_OVERRIDE
636 { 877 {
637 if (IsExistingFolder(path)) 878 boost::recursive_mutex::scoped_lock lock(mutex_);
638 { 879
880 if (IsUploadedFolder(path))
881 {
882 const UploadedFolder* folder = LookupUploadedFolder(path);
883 if (folder == NULL)
884 {
885 return false;
886 }
887 else
888 {
889 folder->ListCollection(collection);
890 return true;
891 }
892 }
893 else if (IsExistingFolder(path))
894 {
895 if (path.empty())
896 {
897 collection.AddResource(new Folder(UPLOAD_FOLDER));
898 }
899
639 for (unsigned int i = 0; i < 5; i++) 900 for (unsigned int i = 0; i < 5; i++)
640 { 901 {
641 std::unique_ptr<File> f(new File("IM" + boost::lexical_cast<std::string>(i) + ".dcm")); 902 std::unique_ptr<File> f(new File("IM" + boost::lexical_cast<std::string>(i) + ".dcm"));
642 f->SetContentLength(1024 * i); 903 f->SetContentLength(1024 * i);
643 f->SetMimeType(MimeType_PlainText); 904 f->SetMimeType(MimeType_PlainText);
646 907
647 for (unsigned int i = 0; i < 5; i++) 908 for (unsigned int i = 0; i < 5; i++)
648 { 909 {
649 collection.AddResource(new Folder("Folder" + boost::lexical_cast<std::string>(i))); 910 collection.AddResource(new Folder("Folder" + boost::lexical_cast<std::string>(i)));
650 } 911 }
651 912
652 return true; 913 return true;
653 } 914 }
654 else 915 else
655 { 916 {
656 return false; 917 return false;
657 } 918 }
658 } 919 }
659 920
660 virtual bool GetFileContent(std::string& content, 921 virtual bool GetFileContent(MimeType& mime,
922 std::string& content,
923 boost::posix_time::ptime& modificationTime,
661 const UriComponents& path) ORTHANC_OVERRIDE 924 const UriComponents& path) ORTHANC_OVERRIDE
662 { 925 {
663 std::string s = "/"; 926 boost::recursive_mutex::scoped_lock lock(mutex_);
664 for (size_t i = 0; i < path.size(); i++) 927
665 { 928 if (path.empty())
666 s += path[i] + "/"; 929 {
667 } 930 return false;
931 }
932 else if (IsUploadedFolder(path))
933 {
934 std::vector<std::string> p(path.begin(), path.end() - 1);
668 935
669 content = "Hello world!\r\n" + s + "\r\n"; 936 const UploadedFolder* folder = LookupUploadedFolder(p);
670 return true; 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 }
955 else if (path.back() == "IM0.dcm" ||
956 path.back() == "IM1.dcm" ||
957 path.back() == "IM2.dcm" ||
958 path.back() == "IM3.dcm" ||
959 path.back() == "IM4.dcm")
960 {
961 modificationTime = boost::posix_time::second_clock::universal_time();
962
963 std::string s;
964 for (size_t i = 0; i < path.size(); i++)
965 {
966 s += "/" + path[i];
967 }
968
969 content = "Hello world!\r\n" + s + "\r\n";
970 mime = MimeType_PlainText;
971 return true;
972 }
973 else
974 {
975 return false;
976 }
977 }
978
979
980 virtual bool StoreFile(const std::string& content,
981 const UriComponents& path) ORTHANC_OVERRIDE
982 {
983 boost::recursive_mutex::scoped_lock lock(mutex_);
984
985 if (IsUploadedFolder(path))
986 {
987 std::vector<std::string> p(path.begin(), path.end() - 1);
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 }
1000 else
1001 {
1002 LOG(WARNING) << "Writing to a read-only location in WebDAV: " << Toolbox::FlattenUri(path);
1003 return false;
1004 }
1005 }
1006
1007
1008 virtual bool CreateFolder(const UriComponents& path)
1009 {
1010 boost::recursive_mutex::scoped_lock lock(mutex_);
1011
1012 if (IsUploadedFolder(path))
1013 {
1014 std::vector<std::string> p(path.begin(), path.end() - 1);
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 }
1027 else
1028 {
1029 LOG(WARNING) << "Writing to a read-only location in WebDAV: " << Toolbox::FlattenUri(path);
1030 return false;
1031 }
1032 }
1033
1034
1035 virtual void Start() ORTHANC_OVERRIDE
1036 {
1037 boost::recursive_mutex::scoped_lock lock(mutex_);
1038
1039 LOG(WARNING) << "Starting WebDAV";
1040 }
1041
1042
1043 virtual void Stop() ORTHANC_OVERRIDE
1044 {
1045 boost::recursive_mutex::scoped_lock lock(mutex_);
1046
1047 LOG(WARNING) << "Stopping WebDAV";
671 } 1048 }
672 }; 1049 };
673 1050
674 1051
675 1052
1109 httpServer.SetIncomingHttpRequestFilter(httpFilter); 1486 httpServer.SetIncomingHttpRequestFilter(httpFilter);
1110 httpServer.SetHttpExceptionFormatter(exceptionFormatter); 1487 httpServer.SetHttpExceptionFormatter(exceptionFormatter);
1111 httpServer.Register(context.GetHttpHandler()); 1488 httpServer.Register(context.GetHttpHandler());
1112 1489
1113 { 1490 {
1114 std::vector<std::string> root; // TODO 1491 UriComponents root; // TODO
1115 root.push_back("a"); 1492 root.push_back("a");
1116 root.push_back("b"); 1493 root.push_back("b");
1117 httpServer.Register(root, new DummyBucket(context)); 1494 httpServer.Register(root, new DummyBucket(context));
1118 } 1495 }
1119 1496