diff 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
line wrap: on
line diff
--- a/OrthancServer/Sources/main.cpp	Mon Oct 05 10:55:24 2020 +0200
+++ b/OrthancServer/Sources/main.cpp	Tue Oct 06 12:45:11 2020 +0200
@@ -613,29 +613,290 @@
 
 
 
+static const char* const UPLOAD_FOLDER = "upload";
+
 class DummyBucket : public IWebDavBucket  // TODO
 {
 private:
   ServerContext& context_;
 
+  static void RemoveFirstElement(std::vector<std::string>& target,
+                                 const std::vector<std::string>& source)
+  {
+    if (source.empty())
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      target.resize(source.size() - 1);
+      for (size_t i = 1; i < source.size(); i++)
+      {
+        target[i - 1] = source[i];
+      }
+    }
+  }
+  
+  class UploadedFile : public boost::noncopyable
+  {
+  private:
+    std::string               content_;
+    MimeType                  mime_;
+    boost::posix_time::ptime  time_;
+
+    void Touch()
+    {
+      time_ = boost::posix_time::second_clock::universal_time();
+    }
+    
+  public:
+    UploadedFile() :
+      mime_(MimeType_Binary)
+    {
+      Touch();
+    }
+    
+    void SetContent(const std::string& content,
+                    MimeType mime)
+    {
+      content_ = content;
+      mime_ = mime;
+      Touch();
+    }
+
+    MimeType GetMimeType() const
+    {
+      return mime_;
+    }
+
+    const std::string& GetContent() const
+    {
+      return content_;
+    }
+
+    const boost::posix_time::ptime& GetTime() const
+    {
+      return time_;
+    }
+  };
+
+
+  class UploadedFolder : public boost::noncopyable
+  {
+  private:
+    typedef std::map<std::string, UploadedFile*>    Files;
+    typedef std::map<std::string, UploadedFolder*>  Subfolders;
+
+    Files       files_;
+    Subfolders  subfolders_;
+
+    void CheckName(const std::string& name)
+    {
+      if (name.empty() ||
+          name.find('/') != std::string::npos ||
+          name.find('\\') != std::string::npos ||
+          name.find('\0') != std::string::npos)
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange,
+                               "Bad resource name for WebDAV: " + name);
+      }
+    }
+
+    bool IsExisting(const std::string& name) const
+    {
+      return (files_.find(name) != files_.end() ||
+              subfolders_.find(name) != subfolders_.end());
+    }
+
+  public:
+    ~UploadedFolder()
+    {
+      for (Files::iterator it = files_.begin(); it != files_.end(); ++it)
+      {
+        assert(it->second != NULL);
+        delete it->second;
+      }        
+
+      for (Subfolders::iterator it = subfolders_.begin(); it != subfolders_.end(); ++it)
+      {
+        assert(it->second != NULL);
+        delete it->second;
+      }        
+    }
+
+    const UploadedFile* LookupFile(const std::string& name) const
+    {
+      Files::const_iterator found = files_.find(name);
+      if (found == files_.end())
+      {
+        return NULL;
+      }
+      else
+      {
+        assert(found->second != NULL);
+        return found->second;
+      }
+    }
+
+    bool CreateSubfolder(const std::string& name)
+    {
+      CheckName(name);
+
+      if (IsExisting(name))
+      {
+        LOG(ERROR) << "WebDAV folder already existing: " << name;
+        return false;
+      }
+      else
+      {
+        subfolders_[name] = new UploadedFolder;
+        return true;
+      }
+    }
+
+    bool StoreFile(const std::string& name,
+                   const std::string& content,
+                   MimeType mime)
+    {
+      CheckName(name);
+
+      if (subfolders_.find(name) != subfolders_.end())
+      {
+        LOG(ERROR) << "WebDAV folder already existing: " << name;
+        return false;
+      }
+
+      Files::iterator found = files_.find(name);
+      if (found == files_.end())
+      {
+        std::unique_ptr<UploadedFile> f(new UploadedFile);
+        f->SetContent(content, mime);
+        files_[name] = f.release();
+      }
+      else
+      {
+        assert(found->second != NULL);
+        found->second->SetContent(content, mime);
+      }
+      
+      return true;
+    }
+
+    UploadedFolder* LookupSubfolder(const UriComponents& path)
+    {
+      if (path.empty())
+      {
+        return this;
+      }
+      else
+      {
+        Subfolders::const_iterator found = subfolders_.find(path[0]);
+        if (found == subfolders_.end())
+        {
+          return NULL;
+        }
+        else
+        {
+          assert(found->second != NULL);
+
+          UriComponents p;
+          RemoveFirstElement(p, path);
+          
+          return found->second->LookupSubfolder(p);
+        }
+      }
+    }
+
+    void ListCollection(Collection& collection) const
+    {
+      for (Files::const_iterator it = files_.begin(); it != files_.end(); ++it)
+      {
+        assert(it->second != NULL);
+        
+        std::unique_ptr<File> f(new File(it->first));
+        f->SetContentLength(it->second->GetContent().size());
+        f->SetCreationTime(it->second->GetTime());
+        collection.AddResource(f.release());
+      }
+
+      for (Subfolders::const_iterator it = subfolders_.begin(); it != subfolders_.end(); ++it)
+      {
+        collection.AddResource(new Folder(it->first));
+      }
+    }
+  };
+
+  bool IsUploadedFolder(const UriComponents& path) const
+  {
+    return (path.size() >= 1 &&
+            path[0] == UPLOAD_FOLDER);
+  }
+
+  UploadedFolder  uploads_;
+  boost::recursive_mutex    mutex_;
+
+  UploadedFolder* LookupUploadedFolder(const UriComponents& path)
+  {
+    if (IsUploadedFolder(path))
+    {
+      UriComponents p;
+      RemoveFirstElement(p, path);
+      
+      return uploads_.LookupSubfolder(p);
+    }
+    else
+    {
+      return NULL;
+    }
+  }
+  
 public:
   DummyBucket(ServerContext& context) :
     context_(context)
   {
   }
   
-  virtual bool IsExistingFolder(const std::vector<std::string>& path) ORTHANC_OVERRIDE
+  virtual bool IsExistingFolder(const UriComponents& path) ORTHANC_OVERRIDE
   {
-    return (path.size() == 0 ||
-            (path.size() == 1 && path[0] == "Folder1") ||
-            (path.size() == 2 && path[0] == "Folder1" && path[1] == "Folder2"));
+    boost::recursive_mutex::scoped_lock lock(mutex_);
+    
+    if (IsUploadedFolder(path))
+    {
+      return LookupUploadedFolder(path) != NULL;
+    }
+    else
+    {
+      return (path.size() == 0 ||
+              (path.size() == 1 && path[0] == "Folder1") ||
+              (path.size() == 2 && path[0] == "Folder1" && path[1] == "Folder2"));
+    }
   }
 
   virtual bool ListCollection(Collection& collection,
                               const UriComponents& path) ORTHANC_OVERRIDE
   {
-    if (IsExistingFolder(path))
+    boost::recursive_mutex::scoped_lock lock(mutex_);
+    
+    if (IsUploadedFolder(path))
     {
+      const UploadedFolder* folder = LookupUploadedFolder(path);
+      if (folder == NULL)
+      {
+        return false;
+      }
+      else
+      {
+        folder->ListCollection(collection);
+        return true;
+      }
+    }
+    else if (IsExistingFolder(path))
+    {
+      if (path.empty())
+      {
+        collection.AddResource(new Folder(UPLOAD_FOLDER));
+      }
+      
       for (unsigned int i = 0; i < 5; i++)
       {
         std::unique_ptr<File> f(new File("IM" + boost::lexical_cast<std::string>(i) + ".dcm"));
@@ -648,7 +909,65 @@
       {
         collection.AddResource(new Folder("Folder" + boost::lexical_cast<std::string>(i)));
       }
-        
+
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  virtual bool GetFileContent(MimeType& mime,
+                              std::string& content,
+                              boost::posix_time::ptime& modificationTime, 
+                              const UriComponents& path) ORTHANC_OVERRIDE
+  {
+    boost::recursive_mutex::scoped_lock lock(mutex_);
+    
+    if (path.empty())
+    {
+      return false;
+    }
+    else if (IsUploadedFolder(path))
+    {
+      std::vector<std::string> p(path.begin(), path.end() - 1);
+      
+      const UploadedFolder* folder = LookupUploadedFolder(p);
+      if (folder == NULL)
+      {
+        return false;
+      }
+
+      const UploadedFile* file = folder->LookupFile(path.back());
+      if (file == NULL)
+      {
+        return false;
+      }
+      else
+      {
+        mime = file->GetMimeType();
+        content = file->GetContent();
+        modificationTime = file->GetTime();
+        return true;
+      }
+    }
+    else if (path.back() == "IM0.dcm" ||
+             path.back() == "IM1.dcm" ||
+             path.back() == "IM2.dcm" ||
+             path.back() == "IM3.dcm" ||
+             path.back() == "IM4.dcm")
+    {
+      modificationTime = boost::posix_time::second_clock::universal_time();
+
+      std::string s;
+      for (size_t i = 0; i < path.size(); i++)
+      {
+        s += "/" + path[i];
+      }
+      
+      content = "Hello world!\r\n" + s + "\r\n";
+      mime = MimeType_PlainText;
       return true;
     }
     else
@@ -657,17 +976,75 @@
     }
   }
 
-  virtual bool GetFileContent(std::string& content,
-                              const UriComponents& path) ORTHANC_OVERRIDE
+  
+  virtual bool StoreFile(const std::string& content,
+                         const UriComponents& path) ORTHANC_OVERRIDE
   {
-    std::string s = "/";
-    for (size_t i = 0; i < path.size(); i++)
+    boost::recursive_mutex::scoped_lock lock(mutex_);
+    
+    if (IsUploadedFolder(path))
+    {
+      std::vector<std::string> p(path.begin(), path.end() - 1);
+      
+      UploadedFolder* folder = LookupUploadedFolder(p);
+      if (folder == NULL)
+      {
+        return false;
+      }
+      else
+      {
+        printf("STORING %d bytes at %s\n", content.size(), path.back().c_str());
+        return folder->StoreFile(path.back(), content, SystemToolbox::AutodetectMimeType(path.back()));
+      }
+    }
+    else
+    {
+      LOG(WARNING) << "Writing to a read-only location in WebDAV: " << Toolbox::FlattenUri(path);
+      return false;
+    }
+  }
+
+
+  virtual bool CreateFolder(const UriComponents& path)
+  {
+    boost::recursive_mutex::scoped_lock lock(mutex_);
+    
+    if (IsUploadedFolder(path))
     {
-      s += path[i] + "/";
-    }
+      std::vector<std::string> p(path.begin(), path.end() - 1);
       
-    content = "Hello world!\r\n" + s + "\r\n";
-    return true;
+      UploadedFolder* folder = LookupUploadedFolder(p);
+      if (folder == NULL)
+      {
+        return false;
+      }
+      else
+      {
+        printf("CREATING FOLDER %s\n", path.back().c_str());
+        return folder->CreateSubfolder(path.back());
+      }
+    }
+    else
+    {
+      LOG(WARNING) << "Writing to a read-only location in WebDAV: " << Toolbox::FlattenUri(path);
+      return false;
+    }
+  }
+
+
+  virtual void Start() ORTHANC_OVERRIDE
+  {
+    boost::recursive_mutex::scoped_lock lock(mutex_);
+    
+    LOG(WARNING) << "Starting WebDAV";
+  }
+
+
+  virtual void Stop() ORTHANC_OVERRIDE
+  {
+    boost::recursive_mutex::scoped_lock lock(mutex_);
+    
+    LOG(WARNING) << "Stopping WebDAV";
   }
 };
 
@@ -1111,7 +1488,7 @@
     httpServer.Register(context.GetHttpHandler());
 
     {
-      std::vector<std::string> root;  // TODO
+      UriComponents root;  // TODO
       root.push_back("a");
       root.push_back("b");
       httpServer.Register(root, new DummyBucket(context));