From 9a2fb917b717b22d3c8f6d2688541c6a9c01cd03 Mon Sep 17 00:00:00 2001 From: stefanbenten Date: Sat, 28 Mar 2020 19:04:33 +0100 Subject: [PATCH] First swing at putting metadata handling into the storage layer --- server/handlers.go | 140 +++++------------- server/server.go | 2 + server/storage.go | 359 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 315 insertions(+), 186 deletions(-) diff --git a/server/handlers.go b/server/handlers.go index 4f0af3d..456ffff 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -32,7 +32,6 @@ import ( "archive/zip" "bytes" "compress/gzip" - "encoding/json" "errors" "fmt" blackfriday "github.com/russross/blackfriday/v2" @@ -92,7 +91,7 @@ func initHTMLTemplates() *html_template.Template { } func healthHandler(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Approaching Neutral Zone, all systems normal and functioning.") + _, _ = w.Write([]byte("Approaching Neutral Zone, all systems normal and functioning.")) } /* The preview handler will show a preview of the content for browsers (accept type text/html), and referer is not transfer.sh */ @@ -111,11 +110,6 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { } contentType := metadata.ContentType - contentLength, err := s.storage.Head(token, filename) - if err != nil { - http.Error(w, http.StatusText(404), 404) - return - } var templatePath string var content html_template.HTML @@ -181,7 +175,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { UrlGet string Hostname string WebAddress string - ContentLength uint64 + ContentLength int64 GAKey string UserVoiceKey string QRCode string @@ -193,7 +187,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { resolvedURLGet, hostname, webAddress, - contentLength, + metadata.ContentLength, s.gaKey, s.userVoiceKey, qrCode, @@ -295,10 +289,10 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { log.Fatal(err) } + defer cleanTmpFile(file) + n, err = io.Copy(file, io.MultiReader(&b, f)) if err != nil { - cleanTmpFile(file) - log.Printf("%s", err.Error()) http.Error(w, err.Error(), 500) return @@ -309,28 +303,11 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { reader = bytes.NewReader(b.Bytes()) } - contentLength := n + metadata := s.metadataForRequest(contentType, n, r) - metadata := MetadataForRequest(contentType, r) - - buffer := &bytes.Buffer{} - if err := json.NewEncoder(buffer).Encode(metadata); err != nil { - log.Printf("%s", err.Error()) - http.Error(w, errors.New("Could not encode metadata").Error(), 500) - - cleanTmpFile(file) - return - } else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil { - log.Printf("%s", err.Error()) - http.Error(w, errors.New("Could not save metadata").Error(), 500) + log.Printf("Uploading %s %s %d %s", token, filename, metadata.ContentLength, metadata.ContentType) - cleanTmpFile(file) - return - } - - log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType) - - if err = s.storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil { + if err = s.storage.Put(token, filename, reader, metadata); err != nil { log.Printf("Backend storage error: %s", err.Error()) http.Error(w, err.Error(), 500) return @@ -339,9 +316,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { filename = url.PathEscape(filename) relativeURL, _ := url.Parse(path.Join(s.proxyPath, token, filename)) - fmt.Fprintln(w, getURL(r).ResolveReference(relativeURL).String()) - - cleanTmpFile(file) + _, _ = fmt.Fprintln(w, getURL(r).ResolveReference(relativeURL).String()) } } } @@ -360,25 +335,11 @@ func cleanTmpFile(f *os.File) { } } -type Metadata struct { - // ContentType is the original uploading content type - ContentType string - // Secret as knowledge to delete file - // Secret string - // Downloads is the actual number of downloads - Downloads int - // MaxDownloads contains the maximum numbers of downloads - 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 { +func (s *Server) metadataForRequest(contentType string, contentLength int64, r *http.Request) Metadata { metadata := Metadata{ ContentType: contentType, - MaxDate: time.Time{}, + ContentLength: contentLength, + MaxDate: time.Now().Add(s.lifetime), Downloads: 0, MaxDownloads: -1, DeletionToken: Encode(10000000+int64(rand.Intn(1000000000))) + Encode(10000000+int64(rand.Intn(1000000000))), @@ -390,12 +351,13 @@ func MetadataForRequest(contentType string, r *http.Request) Metadata { metadata.MaxDownloads = v } - if v := r.Header.Get("Max-Days"); v == "" { - } else if v, err := strconv.Atoi(v); err != nil { - } else { + if maxDays := r.Header.Get("Max-Days"); maxDays != "" { + v, err := strconv.Atoi(maxDays) + if err != nil { + return metadata + } metadata.MaxDate = time.Now().Add(time.Hour * 24 * time.Duration(v)) } - return metadata } @@ -469,24 +431,13 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { token := Encode(10000000 + int64(rand.Intn(1000000000))) - metadata := MetadataForRequest(contentType, r) - - buffer := &bytes.Buffer{} - if err := json.NewEncoder(buffer).Encode(metadata); err != nil { - log.Printf("%s", err.Error()) - http.Error(w, errors.New("Could not encode metadata").Error(), 500) - return - } else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil { - log.Printf("%s", err.Error()) - http.Error(w, errors.New("Could not save metadata").Error(), 500) - return - } + metadata := s.metadataForRequest(contentType, contentLength, r) log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType) var err error - if err = s.storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil { + if err = s.storage.Put(token, filename, reader, metadata); err != nil { log.Printf("Error putting new file: %s", err.Error()) http.Error(w, errors.New("Could not save file").Error(), 500) return @@ -502,7 +453,7 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Url-Delete", resolveURL(r, deleteURL)) - fmt.Fprint(w, resolveURL(r, relativeURL)) + _, _ = fmt.Fprint(w, resolveURL(r, relativeURL)) } func resolveURL(r *http.Request, u *url.URL) string { @@ -613,18 +564,14 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (M var metadata Metadata - r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) + metadata, err := s.storage.Head(token, filename) if s.storage.IsNotExist(err) { return metadata, nil } else if err != nil { return metadata, err } - defer r.Close() - - if err := json.NewDecoder(r).Decode(&metadata); err != nil { - return metadata, err - } else if metadata.MaxDownloads != -1 && metadata.Downloads >= metadata.MaxDownloads { + if metadata.MaxDownloads != -1 && metadata.Downloads >= metadata.MaxDownloads { return metadata, errors.New("MaxDownloads expired.") } else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) { return metadata, errors.New("MaxDate expired.") @@ -636,10 +583,7 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (M metadata.Downloads++ } - buffer := &bytes.Buffer{} - if err := json.NewEncoder(buffer).Encode(metadata); err != nil { - return metadata, errors.New("Could not encode metadata") - } else if err := s.storage.Put(token, fmt.Sprintf("%s.metadata", filename), buffer, "text/json", uint64(buffer.Len())); err != nil { + if err := s.storage.Meta(token, filename, metadata); err != nil { return metadata, errors.New("Could not save metadata") } } @@ -653,18 +597,15 @@ func (s *Server) CheckDeletionToken(deletionToken, token, filename string) error var metadata Metadata - r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) + metadata, err := s.storage.Head(token, filename) if s.storage.IsNotExist(err) { return nil - } else if err != nil { + } + if err != nil { return err } - defer r.Close() - - if err := json.NewDecoder(r).Decode(&metadata); err != nil { - return err - } else if metadata.DeletionToken != deletionToken { + if metadata.DeletionToken != deletionToken { return errors.New("Deletion token doesn't match.") } @@ -774,10 +715,10 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename)) w.Header().Set("Connection", "close") - os := gzip.NewWriter(w) - defer os.Close() + writer := gzip.NewWriter(w) + defer writer.Close() - zw := tar.NewWriter(os) + zw := tar.NewWriter(writer) defer zw.Close() for _, key := range strings.Split(files, ",") { @@ -791,7 +732,7 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) { continue } - reader, contentLength, err := s.storage.Get(token, filename) + reader, metadata, err := s.storage.Get(token, filename) if err != nil { if s.storage.IsNotExist(err) { http.Error(w, "File not found", 404) @@ -807,7 +748,7 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) { header := &tar.Header{ Name: strings.Split(key, "/")[1], - Size: int64(contentLength), + Size: metadata.ContentLength, } err = zw.WriteHeader(header) @@ -850,7 +791,7 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) { continue } - reader, contentLength, err := s.storage.Get(token, filename) + reader, metadata, err := s.storage.Get(token, filename) if err != nil { if s.storage.IsNotExist(err) { http.Error(w, "File not found", 404) @@ -866,7 +807,7 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) { header := &tar.Header{ Name: strings.Split(key, "/")[1], - Size: int64(contentLength), + Size: metadata.ContentLength, } err = zw.WriteHeader(header) @@ -898,8 +839,6 @@ func (s *Server) headHandler(w http.ResponseWriter, r *http.Request) { return } - contentType := metadata.ContentType - contentLength, err := s.storage.Head(token, filename) if s.storage.IsNotExist(err) { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return @@ -911,8 +850,8 @@ func (s *Server) headHandler(w http.ResponseWriter, r *http.Request) { remainingDownloads, remainingDays := metadata.remainingLimitHeaderValues() - w.Header().Set("Content-Type", contentType) - w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10)) + w.Header().Set("Content-Type", metadata.ContentType) + w.Header().Set("Content-Length", strconv.FormatInt(metadata.ContentLength, 10)) w.Header().Set("Connection", "close") w.Header().Set("X-Remaining-Downloads", remainingDownloads) w.Header().Set("X-Remaining-Days", remainingDays) @@ -933,8 +872,7 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) { return } - contentType := metadata.ContentType - reader, contentLength, err := s.storage.Get(token, filename) + reader, _, err := s.storage.Get(token, filename) if s.storage.IsNotExist(err) { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return @@ -956,8 +894,8 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) { remainingDownloads, remainingDays := metadata.remainingLimitHeaderValues() - w.Header().Set("Content-Type", contentType) - w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10)) + w.Header().Set("Content-Type", metadata.ContentType) + w.Header().Set("Content-Length", strconv.FormatInt(metadata.ContentLength, 10)) w.Header().Set("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"", disposition, filename)) w.Header().Set("Connection", "keep-alive") w.Header().Set("X-Remaining-Downloads", remainingDownloads) diff --git a/server/server.go b/server/server.go index 16c3197..ca9d9b7 100644 --- a/server/server.go +++ b/server/server.go @@ -259,6 +259,8 @@ type Server struct { storage Storage + lifetime time.Duration + forceHTTPs bool ipFilterOptions *IPFilterOptions diff --git a/server/storage.go b/server/storage.go index 98ab0fc..73f41e5 100644 --- a/server/storage.go +++ b/server/storage.go @@ -1,6 +1,7 @@ package server import ( + "bytes" "encoding/json" "fmt" "io" @@ -9,7 +10,9 @@ import ( "net/http" "os" "path/filepath" + "strconv" "strings" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" @@ -23,12 +26,31 @@ import ( "google.golang.org/api/googleapi" ) +type Metadata struct { + // ContentType is the original uploading content type + ContentType string + // ContentLength contains the length of the actual object + ContentLength int64 + // Downloads is the actual number of downloads + Downloads int + // MaxDownloads contains the maximum numbers of downloads + MaxDownloads int + // MaxDate contains the max age of the file + MaxDate time.Time + // DeletionToken contains the token to match against for deletion + DeletionToken string + // Secret as knowledge to delete file + Secret string +} + type Storage interface { - Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) - Head(token string, filename string) (contentLength uint64, err error) - Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error + Get(token string, filename string) (reader io.ReadCloser, metaData Metadata, err error) + Head(token string, filename string) (metadata Metadata, err error) + Meta(token string, filename string, metadata Metadata) error + Put(token string, filename string, reader io.Reader, metadata Metadata) error Delete(token string, filename string) error IsNotExist(err error) bool + DeleteExpired() error Type() string } @@ -47,55 +69,56 @@ func (s *LocalStorage) Type() string { return "local" } -func (s *LocalStorage) Head(token string, filename string) (contentLength uint64, err error) { +func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, metadata Metadata, err error) { path := filepath.Join(s.basedir, token, filename) - var fi os.FileInfo - if fi, err = os.Lstat(path); err != nil { - return + // content type , content length + reader, err = os.Open(path) + if err != nil { + return nil, Metadata{}, err } - contentLength = uint64(fi.Size()) - - return + metadata, err = s.Head(token, filename) + if err != nil { + return nil, Metadata{}, err + } + return reader, metadata, nil } -func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { +func (s *LocalStorage) Head(token string, filename string) (metadata Metadata, err error) { path := filepath.Join(s.basedir, token, filename) - // content type , content length - if reader, err = os.Open(path); err != nil { + fi, err := os.Open(path) + if err != nil { return } - var fi os.FileInfo - if fi, err = os.Lstat(path); err != nil { - return + err = json.NewDecoder(fi).Decode(&metadata) + if err != nil { + return Metadata{}, err } - - contentLength = uint64(fi.Size()) - - return + return metadata, nil } -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) Meta(token string, filename string, metadata Metadata) error { + return s.putMetadata(token, filename, metadata) } -func (s *LocalStorage) IsNotExist(err error) bool { - if err == nil { - return false +func (s *LocalStorage) Put(token string, filename string, reader io.Reader, metadata Metadata) error { + err := s.putMetadata(token, filename, metadata) + if err != nil { + return err } - return os.IsNotExist(err) + err = s.put(token, filename, reader) + if err != nil { + //Delete the metadata if the put failed + _ = s.Delete(token, fmt.Sprintf("%s.metadata", filename)) + } + return err } -func (s *LocalStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error { +func (s *LocalStorage) put(token string, filename string, reader io.Reader) error { var f io.WriteCloser var err error @@ -111,10 +134,41 @@ func (s *LocalStorage) Put(token string, filename string, reader io.Reader, cont defer f.Close() - if _, err = io.Copy(f, reader); err != nil { + _, err = io.Copy(f, reader) + return err +} + +func (s *LocalStorage) putMetadata(token string, filename string, metadata Metadata) error { + buffer := &bytes.Buffer{} + if err := json.NewEncoder(buffer).Encode(metadata); err != nil { + log.Printf("%s", err.Error()) return err + } else if err := s.put(token, filename, buffer); err != nil { + log.Printf("%s", err.Error()) + + return nil + } + return nil +} + +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 } + return os.IsNotExist(err) +} + +func (s *LocalStorage) DeleteExpired() error { return nil } @@ -137,7 +191,7 @@ func (s *S3Storage) Type() string { return "s3" } -func (s *S3Storage) Head(token string, filename string) (contentLength uint64, err error) { +func (s *S3Storage) Head(token string, filename string) (metadata Metadata, err error) { key := fmt.Sprintf("%s/%s", token, filename) headRequest := &s3.HeadObjectInput{ @@ -148,32 +202,60 @@ func (s *S3Storage) Head(token string, filename string) (contentLength uint64, e // content type , content length response, err := s.s3.HeadObject(headRequest) if err != nil { - return + return Metadata{}, err } - if response.ContentLength != nil { - contentLength = uint64(*response.ContentLength) + downloads, err := strconv.Atoi(*response.Metadata["downloads"]) + if err != nil { + return Metadata{}, err + } + maxdownloads, err := strconv.Atoi(*response.Metadata["maxDownloads"]) + if err != nil { + return Metadata{}, err + } + expires, err := time.Parse("2020-02-02 02:02:02", *response.Expires) + if err != nil { + return Metadata{}, err } - return + metadata = Metadata{ + ContentType: "", + ContentLength: *response.ContentLength, + Downloads: downloads, + MaxDownloads: maxdownloads, + MaxDate: expires, + DeletionToken: *response.Metadata["deletionToken"], + Secret: *response.Metadata["deletionSecret"], + } + return metadata, nil } -func (s *S3Storage) IsNotExist(err error) bool { - if err == nil { - return false - } +func (s *S3Storage) Meta(token string, filename string, metadata Metadata) error { + key := fmt.Sprintf("%s/%s", token, filename) - if aerr, ok := err.(awserr.Error); ok { - switch aerr.Code() { - case s3.ErrCodeNoSuchKey: - return true - } + input := &s3.CopyObjectInput{ + Bucket: aws.String(s.bucket), + CopySource: aws.String(key), + Key: aws.String(key), + MetadataDirective: aws.String("REPLACE"), + Metadata: map[string]*string{ + "downloads": aws.String(strconv.Itoa(metadata.Downloads)), + "maxDownloads": aws.String(strconv.Itoa(metadata.MaxDownloads)), + "deletionToken": aws.String(metadata.DeletionToken), + "deletionSecret": aws.String(metadata.Secret), + }, + ContentType: aws.String(metadata.ContentType), + Expires: aws.Time(metadata.MaxDate), + } + + _, err := s.s3.CopyObject(input) + if err != nil { + return err } - - return false + return nil } -func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { +func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, metadata Metadata, err error) { key := fmt.Sprintf("%s/%s", token, filename) getRequest := &s3.GetObjectInput{ @@ -186,8 +268,27 @@ func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, co return } - if response.ContentLength != nil { - contentLength = uint64(*response.ContentLength) + downloads, err := strconv.Atoi(*response.Metadata["downloads"]) + if err != nil { + return nil, Metadata{}, err + } + maxdownloads, err := strconv.Atoi(*response.Metadata["maxDownloads"]) + if err != nil { + return nil, Metadata{}, err + } + expires, err := time.Parse("2020-02-02 02:02:02", *response.Expires) + if err != nil { + return nil, Metadata{}, err + } + + metadata = Metadata{ + ContentType: "", + ContentLength: *response.ContentLength, + Downloads: downloads, + MaxDownloads: maxdownloads, + MaxDate: expires, + DeletionToken: *response.Metadata["deletionToken"], + Secret: *response.Metadata["deletionSecret"], } reader = response.Body @@ -217,7 +318,7 @@ func (s *S3Storage) Delete(token string, filename string) (err error) { return } -func (s *S3Storage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) { +func (s *S3Storage) Put(token string, filename string, reader io.Reader, metadata Metadata) (err error) { key := fmt.Sprintf("%s/%s", token, filename) s.logger.Printf("Uploading file %s to S3 Bucket", filename) @@ -238,11 +339,39 @@ func (s *S3Storage) Put(token string, filename string, reader io.Reader, content Bucket: aws.String(s.bucket), Key: aws.String(key), Body: reader, + Metadata: map[string]*string{ + "downloads": aws.String(strconv.Itoa(metadata.Downloads)), + "maxDownloads": aws.String(strconv.Itoa(metadata.MaxDownloads)), + "deletionToken": aws.String(metadata.DeletionToken), + "deletionSecret": aws.String(metadata.Secret), + }, + ContentType: aws.String(metadata.ContentType), + Expires: aws.Time(metadata.MaxDate), }) return } +func (s *S3Storage) IsNotExist(err error) bool { + if err == nil { + return false + } + + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + case s3.ErrCodeNoSuchKey: + return true + } + } + + return false +} + +func (s *S3Storage) DeleteExpired() error { + // not necessary, as S3 has expireDate on files to automatically delete the them + return nil +} + type GDrive struct { service *drive.Service rootId string @@ -385,7 +514,7 @@ func (s *GDrive) Type() string { return "gdrive" } -func (s *GDrive) Head(token string, filename string) (contentLength uint64, err error) { +func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, metadata Metadata, err error) { var fileId string fileId, err = s.findId(filename, token) if err != nil { @@ -393,30 +522,37 @@ func (s *GDrive) Head(token string, filename string) (contentLength uint64, err } var fi *drive.File - if fi, err = s.service.Files.Get(fileId).Fields("size").Do(); err != nil { + fi, err = s.service.Files.Get(fileId).Do() + if !s.hasChecksum(fi) { + err = fmt.Errorf("Cannot find file %s/%s", token, filename) return } - - contentLength = uint64(fi.Size) - - return -} - -func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { - var fileId string - fileId, err = s.findId(filename, token) if err != nil { - return + return nil, Metadata{}, err } - var fi *drive.File - fi, err = s.service.Files.Get(fileId).Fields("size", "md5Checksum").Do() - if !s.hasChecksum(fi) { - err = fmt.Errorf("Cannot find file %s/%s", token, filename) - return + downloads, err := strconv.Atoi(fi.Properties["downloads"]) + if err != nil { + return nil, Metadata{}, err + } + maxdownloads, err := strconv.Atoi(fi.Properties["maxDownloads"]) + if err != nil { + return nil, Metadata{}, err + } + expires, err := time.Parse("2020-02-02 02:02:02", fi.Properties["expires"]) + if err != nil { + return nil, Metadata{}, err } - contentLength = uint64(fi.Size) + metadata = Metadata{ + ContentType: "", + ContentLength: fi.Size, + Downloads: downloads, + MaxDownloads: maxdownloads, + MaxDate: expires, + DeletionToken: fi.Properties["deletionToken"], + Secret: fi.Properties["deletionSecret"], + } ctx := context.Background() var res *http.Response @@ -430,31 +566,49 @@ 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() - +func (s *GDrive) Head(token string, filename string) (metadata Metadata, err error) { var fileId string fileId, err = s.findId(filename, token) if err != nil { return } - err = s.service.Files.Delete(fileId).Do() - return -} + var fi *drive.File + if fi, err = s.service.Files.Get(fileId).Do(); err != nil { + return + } -func (s *GDrive) IsNotExist(err error) bool { + downloads, err := strconv.Atoi(fi.Properties["downloads"]) if err != nil { - if e, ok := err.(*googleapi.Error); ok { - return e.Code == http.StatusNotFound - } + return Metadata{}, err + } + maxdownloads, err := strconv.Atoi(fi.Properties["maxDownloads"]) + if err != nil { + return Metadata{}, err + } + expires, err := time.Parse("2020-02-02 02:02:02", fi.Properties["expires"]) + if err != nil { + return Metadata{}, err } - return false + metadata = Metadata{ + ContentType: "", + ContentLength: fi.Size, + Downloads: downloads, + MaxDownloads: maxdownloads, + MaxDate: expires, + DeletionToken: fi.Properties["deletionToken"], + Secret: fi.Properties["deletionSecret"], + } + + return +} + +func (s *GDrive) Meta(token string, filename string, metadata Metadata) error { + return nil } -func (s *GDrive) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error { +func (s *GDrive) Put(token string, filename string, reader io.Reader, metadata Metadata) error { dirId, err := s.findId("", token) if err != nil { return err @@ -479,7 +633,14 @@ func (s *GDrive) Put(token string, filename string, reader io.Reader, contentTyp dst := &drive.File{ Name: filename, Parents: []string{dirId}, - MimeType: contentType, + MimeType: metadata.ContentType, + Properties: map[string]string{ + "downloads": strconv.Itoa(metadata.Downloads), + "maxDownloads": strconv.Itoa(metadata.MaxDownloads), + "deletionToken": metadata.DeletionToken, + "deletionSecret": metadata.Secret, + "expires": metadata.MaxDate.String(), + }, } ctx := context.Background() @@ -492,6 +653,34 @@ func (s *GDrive) Put(token string, filename string, reader io.Reader, contentTyp return nil } +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 { + if e, ok := err.(*googleapi.Error); ok { + return e.Code == http.StatusNotFound + } + } + + return false +} + +func (s *GDrive) DeleteExpired() error { + return nil +} + // Retrieve a token, saves the token, then returns the generated client. func getGDriveClient(config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client { tokenFile := filepath.Join(localConfigPath, GDriveTokenJsonFile)