From 0c9c6e342475063d8370a7cb4db911d44f216346 Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Sun, 24 Jun 2018 06:46:57 +0200 Subject: [PATCH] ISSUE-44 delete support --- server/handlers.go | 70 ++++++++++++++++++++++++++++++++++++++++++---- server/server.go | 2 ++ server/storage.go | 34 ++++++++++++++++++++++ 3 files changed, 100 insertions(+), 6 deletions(-) diff --git a/server/handlers.go b/server/handlers.go index 1576fa5..01e3ef7 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -297,14 +297,17 @@ type Metadata struct { MaxDownloads int // MaxDate contains the max age of the file MaxDate time.Time + // DeletionToken contains the token to match against for deletion + DeletionToken string } func MetadataForRequest(contentType string, r *http.Request) Metadata { metadata := Metadata{ - ContentType: contentType, - MaxDate: time.Now().Add(time.Hour * 24 * 365 * 10), - Downloads: 0, - MaxDownloads: 99999999, + ContentType: contentType, + MaxDate: time.Now().Add(time.Hour * 24 * 365 * 10), + Downloads: 0, + MaxDownloads: 99999999, + DeletionToken: Encode(10000000 + int64(rand.Intn(1000000000))) + Encode(10000000 + int64(rand.Intn(1000000000))), } if v := r.Header.Get("Max-Downloads"); v == "" { @@ -411,11 +414,14 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") relativeURL, _ := url.Parse(path.Join(token, filename)) + deleteUrl , _ := url.Parse(path.Join(token, filename, metadata.DeletionToken)) - fmt.Fprint(w, escapeFilename(r, relativeURL)) + w.Header().Set("X-Url-Delete", resolveUrl(r, deleteUrl, true)) + + fmt.Fprint(w, resolveUrl(r, relativeURL, false)) } -func escapeFilename(r *http.Request, u *url.URL) string { +func resolveUrl(r *http.Request, u *url.URL, absolutePath bool) string { if u.RawQuery != "" { u.Path = fmt.Sprintf("%s?%s", u.Path, url.QueryEscape(u.RawQuery)) u.RawQuery = "" @@ -426,6 +432,10 @@ func escapeFilename(r *http.Request, u *url.URL) string { u.Fragment = "" } + if absolutePath { + r.URL.Path = "" + } + return getURL(r).ResolveReference(u).String() } @@ -513,6 +523,54 @@ func (s *Server) CheckMetadata(token, filename string) error { return nil } +func (s *Server) CheckDeletionToken(deletionToken, token, filename string) error { + s.Lock(token, filename) + defer s.Unlock(token, filename) + + var metadata Metadata + + r, _, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) + if s.storage.IsNotExist(err) { + return nil + } else if err != nil { + return err + } + + defer r.Close() + + if err := json.NewDecoder(r).Decode(&metadata); err != nil { + return err + } else if metadata.DeletionToken != deletionToken { + return errors.New("Deletion token doesn't match.") + } + + return nil +} + +func (s *Server) deleteHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + token := vars["token"] + filename := vars["filename"] + deletionToken := vars["deletionToken"] + + if err := s.CheckDeletionToken(deletionToken, token, filename); err != nil { + log.Printf("Error metadata: %s", err.Error()) + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return + } + + err := s.storage.Delete(token, filename) + if s.storage.IsNotExist(err) { + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return + } else if err != nil { + log.Printf("%s", err.Error()) + http.Error(w, "Could not delete file.", 500) + return + } +} + func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) diff --git a/server/server.go b/server/server.go index c750b46..d168971 100644 --- a/server/server.go +++ b/server/server.go @@ -323,6 +323,8 @@ func (s *Server) Run() { r.HandleFunc("/", s.postHandler).Methods("POST") // r.HandleFunc("/{page}", viewHandler).Methods("GET") + r.HandleFunc("/{token}/{filename}/{deletionToken}", s.deleteHandler).Methods("DELETE") + r.NotFoundHandler = http.HandlerFunc(s.notFoundHandler) mime.AddExtensionType(".md", "text/x-markdown") diff --git a/server/storage.go b/server/storage.go index 6ea2108..ab9a5e7 100644 --- a/server/storage.go +++ b/server/storage.go @@ -28,6 +28,7 @@ type Storage interface { Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) Head(token string, filename string) (contentType string, contentLength uint64, err error) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error + Delete(token string, filename string) error IsNotExist(err error) bool Type() string @@ -81,6 +82,15 @@ func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, return } +func (s *LocalStorage) Delete(token string, filename string) (err error) { + metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename)) + os.Remove(metadata); + + path := filepath.Join(s.basedir, token, filename) + err = os.Remove(path); + return +} + func (s *LocalStorage) IsNotExist(err error) bool { if err == nil { return false @@ -181,6 +191,16 @@ func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, co return } +func (s *S3Storage) Delete(token string, filename string) (err error) { + metadata := fmt.Sprintf("%s/%s.metadata", token, filename) + s.bucket.Del(metadata) + + key := fmt.Sprintf("%s/%s", token, filename) + err = s.bucket.Del(key) + + return +} + func (s *S3Storage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) { key := fmt.Sprintf("%s/%s", token, filename) @@ -580,6 +600,20 @@ func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, conte return } +func (s *GDrive) Delete(token string, filename string) (err error) { + metadata, _ := s.findId(fmt.Sprintf("%s.metadata", filename), token) + s.service.Files.Delete(metadata).Do() + + var fileId string + fileId, err = s.findId(filename, token) + if err != nil { + return + } + + err = s.service.Files.Delete(fileId).Do() + return +} + func (s *GDrive) IsNotExist(err error) bool { if err == nil { return false