Mercurial > hg > orthanc
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 |