From 9fe5f9a5c957f24c0552459325abc474ffdac2f5 Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Thu, 19 Aug 2021 22:45:30 +0200 Subject: [PATCH] Golint (#404) * golint * remove gitter badge --- README.md | 2 +- cmd/cmd.go | 15 ++-- go.mod | 2 +- go.sum | 2 + server/handlers.go | 105 ++++++++++++++-------------- server/handlers_test.go | 26 +++---- server/ip_filter.go | 38 +++++----- server/server.go | 53 ++++++++++---- server/storage.go | 149 +++++++++++++++++++++++++++------------- server/token.go | 4 +- server/token_test.go | 4 +- server/utils.go | 12 ++-- server/virustotal.go | 1 - 13 files changed, 251 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index 01dfdd21..00378176 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# transfer.sh [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dutchcoders/transfer.sh?utm_source=badge&utm_medium=badge&utm_campaign=&utm_campaign=pr-badge&utm_content=badge) [![Go Report Card](https://goreportcard.com/badge/github.com/dutchcoders/transfer.sh)](https://goreportcard.com/report/github.com/dutchcoders/transfer.sh) [![Docker pulls](https://img.shields.io/docker/pulls/dutchcoders/transfer.sh.svg)](https://hub.docker.com/r/dutchcoders/transfer.sh/) [![Build Status](https://github.com/dutchcoders/transfer.sh/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/dutchcoders/transfer.sh/actions/workflows/test.yml?query=branch%3Amaster) +# transfer.sh [![Go Report Card](https://goreportcard.com/badge/github.com/dutchcoders/transfer.sh)](https://goreportcard.com/report/github.com/dutchcoders/transfer.sh) [![Docker pulls](https://img.shields.io/docker/pulls/dutchcoders/transfer.sh.svg)](https://hub.docker.com/r/dutchcoders/transfer.sh/) [![Build Status](https://github.com/dutchcoders/transfer.sh/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/dutchcoders/transfer.sh/actions/workflows/test.yml?query=branch%3Amaster) Easy and fast file sharing from the command-line. This code contains the server with everything you need to create your own instance. diff --git a/cmd/cmd.go b/cmd/cmd.go index 759cb738..cb8892e5 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -12,6 +12,7 @@ import ( "google.golang.org/api/googleapi" ) +// Version is inject at build time var Version = "0.0.0" var helpTemplate = `NAME: {{.Name}} - {{.Usage}} @@ -282,14 +283,16 @@ var globalFlags = []cli.Flag{ }, } +// Cmd wraps cli.app type Cmd struct { *cli.App } -func VersionAction(c *cli.Context) { +func versionAction(c *cli.Context) { fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh %s: Easy file sharing from the command line", Version))) } +// New is the factory for transfer.sh func New() *Cmd { logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags) @@ -304,7 +307,7 @@ func New() *Cmd { app.Commands = []cli.Command{ { Name: "version", - Action: VersionAction, + Action: versionAction, }, } @@ -403,13 +406,13 @@ func New() *Cmd { } if c.Bool("force-https") { - options = append(options, server.ForceHTTPs()) + options = append(options, server.ForceHTTPS()) } if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" { } else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" { } else { - options = append(options, server.HttpAuthCredentials(httpAuthUser, httpAuthPass)) + options = append(options, server.HTTPAuthCredentials(httpAuthUser, httpAuthPass)) } applyIPFilter := false @@ -445,13 +448,13 @@ func New() *Cmd { case "gdrive": chunkSize := c.Int("gdrive-chunk-size") - if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" { + if clientJSONFilepath := c.String("gdrive-client-json-filepath"); clientJSONFilepath == "" { panic("client-json-filepath not set.") } else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" { panic("local-config-path not set.") } else if basedir := c.String("basedir"); basedir == "" { panic("basedir not set.") - } else if storage, err := server.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil { + } else if storage, err := server.NewGDriveStorage(clientJSONFilepath, localConfigPath, basedir, chunkSize, logger); err != nil { panic(err) } else { options = append(options, server.UseStorage(storage)) diff --git a/go.mod b/go.mod index 8a8d76c3..6d252ea2 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329 - github.com/dutchcoders/transfer.sh-web v0.0.0-20210723094506-f0946ebceb7a + github.com/dutchcoders/transfer.sh-web v0.0.0-20210819203540-bbdd40be1311 github.com/elazarl/go-bindata-assetfs v1.0.1 github.com/fatih/color v1.10.0 github.com/garyburd/redigo v1.6.2 // indirect diff --git a/go.sum b/go.sum index f7cfede1..5dee6001 100644 --- a/go.sum +++ b/go.sum @@ -86,6 +86,8 @@ github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329 h1:ERqCk github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329/go.mod h1:G5qOfE5bQZ5scycLpB7fYWgN4y3xdfXo+pYWM8z2epY= github.com/dutchcoders/transfer.sh-web v0.0.0-20210723094506-f0946ebceb7a h1:+N7J1NK7gxKZ+X4syY1HqafUudJiR8voJGcXWkxLgAw= github.com/dutchcoders/transfer.sh-web v0.0.0-20210723094506-f0946ebceb7a/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI= +github.com/dutchcoders/transfer.sh-web v0.0.0-20210819203540-bbdd40be1311 h1:/Rwuhcp8ZLUauWajAgMyy6AiVbobvD52I+/OnzThK0A= +github.com/dutchcoders/transfer.sh-web v0.0.0-20210819203540-bbdd40be1311/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI= github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= diff --git a/server/handlers.go b/server/handlers.go index 4947b770..6d33e5d7 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -123,7 +123,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { token := vars["token"] filename := vars["filename"] - metadata, err := s.CheckMetadata(token, filename, false) + metadata, err := s.checkMetadata(token, filename, false) if err != nil { s.logger.Printf("Error metadata: %s", err.Error()) @@ -198,9 +198,9 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) { ContentType string Content html_template.HTML Filename string - Url string - UrlGet string - UrlRandomToken string + URL string + URLGet string + URLRandomToken string Hostname string WebAddress string ContentLength uint64 @@ -264,8 +264,8 @@ func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) { s.userVoiceKey, purgeTime, maxUploadSize, - Token(s.randomTokenLength), - Token(s.randomTokenLength), + token(s.randomTokenLength), + token(s.randomTokenLength), } if acceptsHTML(r.Header) { @@ -296,7 +296,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { return } - token := Token(s.randomTokenLength) + token := token(s.randomTokenLength) w.Header().Set("Content-Type", "text/plain") @@ -354,7 +354,7 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) { return } - metadata := MetadataForRequest(contentType, s.randomTokenLength, r) + metadata := metadataForRequest(contentType, s.randomTokenLength, r) buffer := &bytes.Buffer{} if err := json.NewEncoder(buffer).Encode(metadata); err != nil { @@ -403,7 +403,7 @@ func (s *Server) cleanTmpFile(f *os.File) { } } -type Metadata struct { +type metadata struct { // ContentType is the original uploading content type ContentType string // Secret as knowledge to delete file @@ -418,13 +418,13 @@ type Metadata struct { DeletionToken string } -func MetadataForRequest(contentType string, randomTokenLength int, r *http.Request) Metadata { - metadata := Metadata{ +func metadataForRequest(contentType string, randomTokenLength int, r *http.Request) metadata { + metadata := metadata{ ContentType: strings.ToLower(contentType), MaxDate: time.Time{}, Downloads: 0, MaxDownloads: -1, - DeletionToken: Token(randomTokenLength) + Token(randomTokenLength), + DeletionToken: token(randomTokenLength) + token(randomTokenLength), } if v := r.Header.Get("Max-Downloads"); v == "" { @@ -512,9 +512,9 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { contentType := mime.TypeByExtension(filepath.Ext(vars["filename"])) - token := Token(s.randomTokenLength) + token := token(s.randomTokenLength) - metadata := MetadataForRequest(contentType, s.randomTokenLength, r) + metadata := metadataForRequest(contentType, s.randomTokenLength, r) buffer := &bytes.Buffer{} if err := json.NewEncoder(buffer).Encode(metadata); err != nil { @@ -639,7 +639,7 @@ func getURL(r *http.Request, proxyPort string) *url.URL { return u } -func (metadata Metadata) remainingLimitHeaderValues() (remainingDownloads, remainingDays string) { +func (metadata metadata) remainingLimitHeaderValues() (remainingDownloads, remainingDays string) { if metadata.MaxDate.IsZero() { remainingDays = "n/a" } else { @@ -656,7 +656,7 @@ func (metadata Metadata) remainingLimitHeaderValues() (remainingDownloads, remai return remainingDownloads, remainingDays } -func (s *Server) Lock(token, filename string) { +func (s *Server) lock(token, filename string) { key := path.Join(token, filename) lock, _ := s.locks.LoadOrStore(key, &sync.Mutex{}) @@ -666,7 +666,7 @@ func (s *Server) Lock(token, filename string) { return } -func (s *Server) Unlock(token, filename string) { +func (s *Server) unlock(token, filename string) { key := path.Join(token, filename) lock, _ := s.locks.LoadOrStore(key, &sync.Mutex{}) @@ -674,11 +674,11 @@ func (s *Server) Unlock(token, filename string) { lock.(*sync.Mutex).Unlock() } -func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (Metadata, error) { - s.Lock(token, filename) - defer s.Unlock(token, filename) +func (s *Server) checkMetadata(token, filename string, increaseDownload bool) (metadata, error) { + s.lock(token, filename) + defer s.unlock(token, filename) - var metadata Metadata + var metadata metadata r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) if err != nil { @@ -690,9 +690,9 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (M if err := json.NewDecoder(r).Decode(&metadata); err != nil { return metadata, err } else if metadata.MaxDownloads != -1 && metadata.Downloads >= metadata.MaxDownloads { - return metadata, errors.New("MaxDownloads expired.") + return metadata, errors.New("maxDownloads expired") } else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) { - return metadata, errors.New("MaxDate expired.") + return metadata, errors.New("maxDate expired") } else if metadata.MaxDownloads != -1 && increaseDownload { // todo(nl5887): mutex? @@ -710,15 +710,15 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (M return metadata, nil } -func (s *Server) CheckDeletionToken(deletionToken, token, filename string) error { - s.Lock(token, filename) - defer s.Unlock(token, filename) +func (s *Server) checkDeletionToken(deletionToken, token, filename string) error { + s.lock(token, filename) + defer s.unlock(token, filename) - var metadata Metadata + var metadata metadata r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) if s.storage.IsNotExist(err) { - return errors.New("Metadata doesn't exist") + return errors.New("metadata doesn't exist") } else if err != nil { return err } @@ -728,7 +728,7 @@ func (s *Server) CheckDeletionToken(deletionToken, token, filename string) error 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 errors.New("deletion token doesn't match") } return nil @@ -754,7 +754,7 @@ func (s *Server) deleteHandler(w http.ResponseWriter, r *http.Request) { filename := vars["filename"] deletionToken := vars["deletionToken"] - if err := s.CheckDeletionToken(deletionToken, token, filename); err != nil { + if err := s.checkDeletionToken(deletionToken, token, filename); err != nil { s.logger.Printf("Error metadata: %s", err.Error()) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return @@ -790,7 +790,7 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) { token := strings.Split(key, "/")[0] filename := sanitize(strings.Split(key, "/")[1]) - if _, err := s.CheckMetadata(token, filename, true); err != nil { + if _, err := s.checkMetadata(token, filename, true); err != nil { s.logger.Printf("Error metadata: %s", err.Error()) continue } @@ -801,11 +801,11 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) { if s.storage.IsNotExist(err) { http.Error(w, "File not found", 404) return - } else { - s.logger.Printf("%s", err.Error()) - http.Error(w, "Could not retrieve file.", 500) - return } + + s.logger.Printf("%s", err.Error()) + http.Error(w, "Could not retrieve file.", 500) + return } defer reader.Close() @@ -862,7 +862,7 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) { token := strings.Split(key, "/")[0] filename := sanitize(strings.Split(key, "/")[1]) - if _, err := s.CheckMetadata(token, filename, true); err != nil { + if _, err := s.checkMetadata(token, filename, true); err != nil { s.logger.Printf("Error metadata: %s", err.Error()) continue } @@ -872,11 +872,11 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) { if s.storage.IsNotExist(err) { http.Error(w, "File not found", 404) return - } else { - s.logger.Printf("%s", err.Error()) - http.Error(w, "Could not retrieve file.", 500) - return } + + s.logger.Printf("%s", err.Error()) + http.Error(w, "Could not retrieve file.", 500) + return } defer reader.Close() @@ -921,7 +921,7 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) { token := strings.Split(key, "/")[0] filename := strings.Split(key, "/")[1] - if _, err := s.CheckMetadata(token, filename, true); err != nil { + if _, err := s.checkMetadata(token, filename, true); err != nil { s.logger.Printf("Error metadata: %s", err.Error()) continue } @@ -931,11 +931,11 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) { if s.storage.IsNotExist(err) { http.Error(w, "File not found", 404) return - } else { - s.logger.Printf("%s", err.Error()) - http.Error(w, "Could not retrieve file.", 500) - return } + + s.logger.Printf("%s", err.Error()) + http.Error(w, "Could not retrieve file.", 500) + return } defer reader.Close() @@ -966,7 +966,7 @@ func (s *Server) headHandler(w http.ResponseWriter, r *http.Request) { token := vars["token"] filename := vars["filename"] - metadata, err := s.CheckMetadata(token, filename, false) + metadata, err := s.checkMetadata(token, filename, false) if err != nil { s.logger.Printf("Error metadata: %s", err.Error()) @@ -1001,7 +1001,7 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) { token := vars["token"] filename := vars["filename"] - metadata, err := s.CheckMetadata(token, filename, true) + metadata, err := s.checkMetadata(token, filename, true) if err != nil { s.logger.Printf("Error metadata: %s", err.Error()) @@ -1073,9 +1073,10 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) { return } +// RedirectHandler handles redirect func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - if !s.forceHTTPs { + if !s.forceHTTPS { // we don't want to enforce https } else if r.URL.Path == "/health.html" { // health check url won't redirect @@ -1095,17 +1096,17 @@ func (s *Server) RedirectHandler(h http.Handler) http.HandlerFunc { } } -// Create a log handler for every request it receives. +// LoveHandler Create a log handler for every request it receives. func LoveHandler(h http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("x-made-with", "<3 by DutchCoders") w.Header().Set("x-served-by", "Proudly served by DutchCoders") - w.Header().Set("Server", "Transfer.sh HTTP Server 1.0") + w.Header().Set("server", "Transfer.sh HTTP Server") h.ServeHTTP(w, r) } } -func IPFilterHandler(h http.Handler, ipFilterOptions *IPFilterOptions) http.HandlerFunc { +func ipFilterHandler(h http.Handler, ipFilterOptions *IPFilterOptions) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if ipFilterOptions == nil { h.ServeHTTP(w, r) @@ -1116,7 +1117,7 @@ func IPFilterHandler(h http.Handler, ipFilterOptions *IPFilterOptions) http.Hand } } -func (s *Server) BasicAuthHandler(h http.Handler) http.HandlerFunc { +func (s *Server) basicAuthHandler(h http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if s.AuthUser == "" || s.AuthPass == "" { h.ServeHTTP(w, r) diff --git a/server/handlers_test.go b/server/handlers_test.go index ae0113ac..2c76ffdf 100644 --- a/server/handlers_test.go +++ b/server/handlers_test.go @@ -13,16 +13,16 @@ import ( func Test(t *testing.T) { TestingT(t) } var ( - _ = Suite(&SuiteRedirectWithForceHTTPs{}) - _ = Suite(&SuiteRedirectWithoutForceHTTPs{}) + _ = Suite(&suiteRedirectWithForceHTTPS{}) + _ = Suite(&suiteRedirectWithoutForceHTTPS{}) ) -type SuiteRedirectWithForceHTTPs struct { +type suiteRedirectWithForceHTTPS struct { handler http.HandlerFunc } -func (s *SuiteRedirectWithForceHTTPs) SetUpTest(c *C) { - srvr, err := New(ForceHTTPs()) +func (s *suiteRedirectWithForceHTTPS) SetUpTest(c *C) { + srvr, err := New(ForceHTTPS()) c.Assert(err, IsNil) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -32,7 +32,7 @@ func (s *SuiteRedirectWithForceHTTPs) SetUpTest(c *C) { s.handler = srvr.RedirectHandler(handler) } -func (s *SuiteRedirectWithForceHTTPs) TestHTTPs(c *C) { +func (s *suiteRedirectWithForceHTTPS) TestHTTPs(c *C) { req := httptest.NewRequest("GET", "https://test/test", nil) w := httptest.NewRecorder() @@ -42,7 +42,7 @@ func (s *SuiteRedirectWithForceHTTPs) TestHTTPs(c *C) { c.Assert(resp.StatusCode, Equals, http.StatusOK) } -func (s *SuiteRedirectWithForceHTTPs) TestOnion(c *C) { +func (s *suiteRedirectWithForceHTTPS) TestOnion(c *C) { req := httptest.NewRequest("GET", "http://test.onion/test", nil) w := httptest.NewRecorder() @@ -52,7 +52,7 @@ func (s *SuiteRedirectWithForceHTTPs) TestOnion(c *C) { c.Assert(resp.StatusCode, Equals, http.StatusOK) } -func (s *SuiteRedirectWithForceHTTPs) TestXForwardedFor(c *C) { +func (s *suiteRedirectWithForceHTTPS) TestXForwardedFor(c *C) { req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil) req.Header.Set("X-Forwarded-Proto", "https") @@ -63,7 +63,7 @@ func (s *SuiteRedirectWithForceHTTPs) TestXForwardedFor(c *C) { c.Assert(resp.StatusCode, Equals, http.StatusOK) } -func (s *SuiteRedirectWithForceHTTPs) TestHTTP(c *C) { +func (s *suiteRedirectWithForceHTTPS) TestHTTP(c *C) { req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil) w := httptest.NewRecorder() @@ -74,11 +74,11 @@ func (s *SuiteRedirectWithForceHTTPs) TestHTTP(c *C) { c.Assert(resp.Header.Get("Location"), Equals, "https://127.0.0.1/test") } -type SuiteRedirectWithoutForceHTTPs struct { +type suiteRedirectWithoutForceHTTPS struct { handler http.HandlerFunc } -func (s *SuiteRedirectWithoutForceHTTPs) SetUpTest(c *C) { +func (s *suiteRedirectWithoutForceHTTPS) SetUpTest(c *C) { srvr, err := New() c.Assert(err, IsNil) @@ -89,7 +89,7 @@ func (s *SuiteRedirectWithoutForceHTTPs) SetUpTest(c *C) { s.handler = srvr.RedirectHandler(handler) } -func (s *SuiteRedirectWithoutForceHTTPs) TestHTTP(c *C) { +func (s *suiteRedirectWithoutForceHTTPS) TestHTTP(c *C) { req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil) w := httptest.NewRecorder() @@ -99,7 +99,7 @@ func (s *SuiteRedirectWithoutForceHTTPs) TestHTTP(c *C) { c.Assert(resp.StatusCode, Equals, http.StatusOK) } -func (s *SuiteRedirectWithoutForceHTTPs) TestHTTPs(c *C) { +func (s *suiteRedirectWithoutForceHTTPS) TestHTTPs(c *C) { req := httptest.NewRequest("GET", "https://127.0.0.1/test", nil) w := httptest.NewRecorder() diff --git a/server/ip_filter.go b/server/ip_filter.go index 6dfc21c7..62c2be9c 100644 --- a/server/ip_filter.go +++ b/server/ip_filter.go @@ -21,7 +21,7 @@ import ( "github.com/tomasen/realip" ) -//IPFilterOptions for IPFilter. Allowed takes precendence over Blocked. +//IPFilterOptions for ipFilter. Allowed takes precedence over Blocked. //IPs can be IPv4 or IPv6 and can optionally contain subnet //masks (/24). Note however, determining if a given IP is //included in a subnet requires a linear scan so is less performant @@ -43,7 +43,8 @@ type IPFilterOptions struct { } } -type IPFilter struct { +// ipFilter +type ipFilter struct { opts IPFilterOptions //mut protects the below //rw since writes are rare @@ -59,13 +60,12 @@ type subnet struct { allowed bool } -//New constructs IPFilter instance. -func NewIPFilter(opts IPFilterOptions) *IPFilter { +func newIPFilter(opts IPFilterOptions) *ipFilter { if opts.Logger == nil { flags := log.LstdFlags opts.Logger = log.New(os.Stdout, "", flags) } - f := &IPFilter{ + f := &ipFilter{ opts: opts, ips: map[string]bool{}, defaultAllowed: !opts.BlockByDefault, @@ -79,15 +79,15 @@ func NewIPFilter(opts IPFilterOptions) *IPFilter { return f } -func (f *IPFilter) AllowIP(ip string) bool { +func (f *ipFilter) AllowIP(ip string) bool { return f.ToggleIP(ip, true) } -func (f *IPFilter) BlockIP(ip string) bool { +func (f *ipFilter) BlockIP(ip string) bool { return f.ToggleIP(ip, false) } -func (f *IPFilter) ToggleIP(str string, allowed bool) bool { +func (f *ipFilter) ToggleIP(str string, allowed bool) bool { //check if has subnet if ip, net, err := net.ParseCIDR(str); err == nil { // containing only one ip? @@ -128,19 +128,19 @@ func (f *IPFilter) ToggleIP(str string, allowed bool) bool { } //ToggleDefault alters the default setting -func (f *IPFilter) ToggleDefault(allowed bool) { +func (f *ipFilter) ToggleDefault(allowed bool) { f.mut.Lock() f.defaultAllowed = allowed f.mut.Unlock() } //Allowed returns if a given IP can pass through the filter -func (f *IPFilter) Allowed(ipstr string) bool { +func (f *ipFilter) Allowed(ipstr string) bool { return f.NetAllowed(net.ParseIP(ipstr)) } //NetAllowed returns if a given net.IP can pass through the filter -func (f *IPFilter) NetAllowed(ip net.IP) bool { +func (f *ipFilter) NetAllowed(ip net.IP) bool { //invalid ip if ip == nil { return false @@ -173,35 +173,35 @@ func (f *IPFilter) NetAllowed(ip net.IP) bool { } //Blocked returns if a given IP can NOT pass through the filter -func (f *IPFilter) Blocked(ip string) bool { +func (f *ipFilter) Blocked(ip string) bool { return !f.Allowed(ip) } //NetBlocked returns if a given net.IP can NOT pass through the filter -func (f *IPFilter) NetBlocked(ip net.IP) bool { +func (f *ipFilter) NetBlocked(ip net.IP) bool { return !f.NetAllowed(ip) } //WrapIPFilter the provided handler with simple IP blocking middleware //using this IP filter and its configuration -func (f *IPFilter) Wrap(next http.Handler) http.Handler { - return &ipFilterMiddleware{IPFilter: f, next: next} +func (f *ipFilter) Wrap(next http.Handler) http.Handler { + return &ipFilterMiddleware{ipFilter: f, next: next} } -//WrapIPFilter is equivalent to NewIPFilter(opts) then Wrap(next) +//WrapIPFilter is equivalent to newIPFilter(opts) then Wrap(next) func WrapIPFilter(next http.Handler, opts IPFilterOptions) http.Handler { - return NewIPFilter(opts).Wrap(next) + return newIPFilter(opts).Wrap(next) } type ipFilterMiddleware struct { - *IPFilter + *ipFilter next http.Handler } func (m *ipFilterMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { remoteIP := realip.FromRequest(r) - if !m.IPFilter.Allowed(remoteIP) { + if !m.ipFilter.Allowed(remoteIP) { //show simple forbidden text http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) return diff --git a/server/server.go b/server/server.go index ea0a0dec..90d66e63 100644 --- a/server/server.go +++ b/server/server.go @@ -48,6 +48,7 @@ import ( "github.com/VojtechVitek/ratelimit/memory" "github.com/gorilla/mux" + // import pprof _ "net/http/pprof" "crypto/tls" @@ -59,28 +60,30 @@ import ( "path/filepath" ) -const SERVER_INFO = "transfer.sh" - // parse request with maximum memory of _24Kilobits const _24K = (1 << 3) * 24 // parse request with maximum memory of _5Megabytes const _5M = (1 << 20) * 5 +// OptionFn is the option function type type OptionFn func(*Server) +// ClamavHost sets clamav host func ClamavHost(s string) OptionFn { return func(srvr *Server) { srvr.ClamAVDaemonHost = s } } +// VirustotalKey sets virus total key func VirustotalKey(s string) OptionFn { return func(srvr *Server) { srvr.VirusTotalKey = s } } +// Listener set listener func Listener(s string) OptionFn { return func(srvr *Server) { srvr.ListenerString = s @@ -88,6 +91,7 @@ func Listener(s string) OptionFn { } +// CorsDomains sets CORS domains func CorsDomains(s string) OptionFn { return func(srvr *Server) { srvr.CorsDomains = s @@ -95,18 +99,21 @@ func CorsDomains(s string) OptionFn { } +// GoogleAnalytics sets GA key func GoogleAnalytics(gaKey string) OptionFn { return func(srvr *Server) { srvr.gaKey = gaKey } } +// UserVoice sets UV key func UserVoice(userVoiceKey string) OptionFn { return func(srvr *Server) { srvr.userVoiceKey = userVoiceKey } } +// TLSListener sets TLS listener and option func TLSListener(s string, t bool) OptionFn { return func(srvr *Server) { srvr.TLSListenerString = s @@ -115,12 +122,14 @@ func TLSListener(s string, t bool) OptionFn { } +// ProfileListener sets profile listener func ProfileListener(s string) OptionFn { return func(srvr *Server) { srvr.ProfileListenerString = s } } +// WebPath sets web path func WebPath(s string) OptionFn { return func(srvr *Server) { if s[len(s)-1:] != "/" { @@ -131,6 +140,7 @@ func WebPath(s string) OptionFn { } } +// ProxyPath sets proxy path func ProxyPath(s string) OptionFn { return func(srvr *Server) { if s[len(s)-1:] != "/" { @@ -141,12 +151,14 @@ func ProxyPath(s string) OptionFn { } } +// ProxyPort sets proxy port func ProxyPort(s string) OptionFn { return func(srvr *Server) { srvr.proxyPort = s } } +// TempPath sets temp path func TempPath(s string) OptionFn { return func(srvr *Server) { if s[len(s)-1:] != "/" { @@ -157,6 +169,7 @@ func TempPath(s string) OptionFn { } } +// LogFile sets log file func LogFile(logger *log.Logger, s string) OptionFn { return func(srvr *Server) { f, err := os.OpenFile(s, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) @@ -169,30 +182,36 @@ func LogFile(logger *log.Logger, s string) OptionFn { } } +// Logger sets logger func Logger(logger *log.Logger) OptionFn { return func(srvr *Server) { srvr.logger = logger } } +// MaxUploadSize sets max upload size func MaxUploadSize(kbytes int64) OptionFn { return func(srvr *Server) { srvr.maxUploadSize = kbytes * 1024 } } + +// RateLimit set rate limit func RateLimit(requests int) OptionFn { return func(srvr *Server) { srvr.rateLimitRequests = requests } } +// RandomTokenLength sets random token length func RandomTokenLength(length int) OptionFn { return func(srvr *Server) { srvr.randomTokenLength = length } } +// Purge sets purge days and option func Purge(days, interval int) OptionFn { return func(srvr *Server) { srvr.purgeDays = time.Duration(days) * time.Hour * 24 @@ -200,24 +219,28 @@ func Purge(days, interval int) OptionFn { } } -func ForceHTTPs() OptionFn { +// ForceHTTPS sets forcing https +func ForceHTTPS() OptionFn { return func(srvr *Server) { - srvr.forceHTTPs = true + srvr.forceHTTPS = true } } +// EnableProfiler sets enable profiler func EnableProfiler() OptionFn { return func(srvr *Server) { srvr.profilerEnabled = true } } +// UseStorage set storage to use func UseStorage(s Storage) OptionFn { return func(srvr *Server) { srvr.storage = s } } +// UseLetsEncrypt set letsencrypt usage func UseLetsEncrypt(hosts []string) OptionFn { return func(srvr *Server) { cacheDir := "./cache/" @@ -246,6 +269,7 @@ func UseLetsEncrypt(hosts []string) OptionFn { } } +// TLSConfig sets TLS config func TLSConfig(cert, pk string) OptionFn { certificate, err := tls.LoadX509KeyPair(cert, pk) return func(srvr *Server) { @@ -257,13 +281,15 @@ func TLSConfig(cert, pk string) OptionFn { } } -func HttpAuthCredentials(user string, pass string) OptionFn { +// HTTPAuthCredentials sets basic http auth credentials +func HTTPAuthCredentials(user string, pass string) OptionFn { return func(srvr *Server) { srvr.AuthUser = user srvr.AuthPass = pass } } +// FilterOptions sets ip filtering func FilterOptions(options IPFilterOptions) OptionFn { for i, allowedIP := range options.AllowedIPs { options.AllowedIPs[i] = strings.TrimSpace(allowedIP) @@ -278,6 +304,7 @@ func FilterOptions(options IPFilterOptions) OptionFn { } } +// Server is the main application type Server struct { AuthUser string AuthPass string @@ -298,7 +325,7 @@ type Server struct { storage Storage - forceHTTPs bool + forceHTTPS bool randomTokenLength int @@ -327,6 +354,7 @@ type Server struct { LetsEncryptCache string } +// New is the factory fot Server func New(options ...OptionFn) (*Server, error) { s := &Server{ locks: sync.Map{}, @@ -347,6 +375,7 @@ func init() { rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:]))) } +// Run starts Server func (s *Server) Run() { listening := false @@ -402,7 +431,7 @@ func (s *Server) Run() { r.HandleFunc("/favicon.ico", staticHandler.ServeHTTP).Methods("GET") r.HandleFunc("/robots.txt", staticHandler.ServeHTTP).Methods("GET") - r.HandleFunc("/{filename:(?:favicon\\.ico|robots\\.txt|health\\.html)}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT") + r.HandleFunc("/{filename:(?:favicon\\.ico|robots\\.txt|health\\.html)}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT") r.HandleFunc("/health.html", healthHandler).Methods("GET") r.HandleFunc("/", s.viewHandler).Methods("GET") @@ -446,10 +475,10 @@ func (s *Server) Run() { r.HandleFunc("/{filename}/virustotal", s.virusTotalHandler).Methods("PUT") r.HandleFunc("/{filename}/scan", s.scanHandler).Methods("PUT") - r.HandleFunc("/put/{filename}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT") - r.HandleFunc("/upload/{filename}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT") - r.HandleFunc("/{filename}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT") - r.HandleFunc("/", s.BasicAuthHandler(http.HandlerFunc(s.postHandler))).Methods("POST") + r.HandleFunc("/put/{filename}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT") + r.HandleFunc("/upload/{filename}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT") + r.HandleFunc("/{filename}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT") + r.HandleFunc("/", s.basicAuthHandler(http.HandlerFunc(s.postHandler))).Methods("POST") // r.HandleFunc("/{page}", viewHandler).Methods("GET") r.HandleFunc("/{token}/{filename}/{deletionToken}", s.deleteHandler).Methods("DELETE") @@ -474,7 +503,7 @@ func (s *Server) Run() { } h := handlers.PanicHandler( - IPFilterHandler( + ipFilterHandler( handlers.LogHandler( LoveHandler( s.RedirectHandler(cors(r))), diff --git a/server/storage.go b/server/storage.go index a0cdbe48..3daf8297 100644 --- a/server/storage.go +++ b/server/storage.go @@ -27,31 +27,43 @@ import ( "storj.io/uplink" ) +// Storage is the interface for storage operation type Storage interface { + // Get retrieves a file from storage Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) + // Head retrieves content length of a file from storage Head(token string, filename string) (contentLength uint64, err error) + // Put saves a file on storage Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error + // Delete removes a file from storage Delete(token string, filename string) error + // IsNotExist indicates if a file doesn't exist on storage IsNotExist(err error) bool + // Purge cleans up the storage Purge(days time.Duration) error + // Type returns the storage type Type() string } +// LocalStorage is a local storage type LocalStorage struct { Storage basedir string logger *log.Logger } +// NewLocalStorage is the factory for LocalStorage func NewLocalStorage(basedir string, logger *log.Logger) (*LocalStorage, error) { return &LocalStorage{basedir: basedir, logger: logger}, nil } +// Type returns the storage type func (s *LocalStorage) Type() string { return "local" } +// Head retrieves content length of a file from storage func (s *LocalStorage) Head(token string, filename string) (contentLength uint64, err error) { path := filepath.Join(s.basedir, token, filename) @@ -65,6 +77,7 @@ func (s *LocalStorage) Head(token string, filename string) (contentLength uint64 return } +// Get retrieves a file from storage func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { path := filepath.Join(s.basedir, token, filename) @@ -83,6 +96,7 @@ func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, return } +// Delete removes a file from storage func (s *LocalStorage) Delete(token string, filename string) (err error) { metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename)) os.Remove(metadata) @@ -92,6 +106,7 @@ func (s *LocalStorage) Delete(token string, filename string) (err error) { return } +// Purge cleans up the storage func (s *LocalStorage) Purge(days time.Duration) (err error) { err = filepath.Walk(s.basedir, func(path string, info os.FileInfo, err error) error { @@ -113,6 +128,7 @@ func (s *LocalStorage) Purge(days time.Duration) (err error) { return } +// IsNotExist indicates if a file doesn't exist on storage func (s *LocalStorage) IsNotExist(err error) bool { if err == nil { return false @@ -121,6 +137,7 @@ func (s *LocalStorage) IsNotExist(err error) bool { return os.IsNotExist(err) } +// Put saves a file on storage func (s *LocalStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error { var f io.WriteCloser var err error @@ -144,6 +161,7 @@ func (s *LocalStorage) Put(token string, filename string, reader io.Reader, cont return nil } +// S3Storage is a storage backed by AWS S3 type S3Storage struct { Storage bucket string @@ -154,6 +172,7 @@ type S3Storage struct { noMultipart bool } +// NewS3Storage is the factory for S3Storage func NewS3Storage(accessKey, secretKey, bucketName string, purgeDays int, region, endpoint string, disableMultipart bool, forcePathStyle bool, logger *log.Logger) (*S3Storage, error) { sess := getAwsSession(accessKey, secretKey, region, endpoint, forcePathStyle) @@ -167,10 +186,12 @@ func NewS3Storage(accessKey, secretKey, bucketName string, purgeDays int, region }, nil } +// Type returns the storage type func (s *S3Storage) Type() string { return "s3" } +// Head retrieves content length of a file from storage func (s *S3Storage) Head(token string, filename string) (contentLength uint64, err error) { key := fmt.Sprintf("%s/%s", token, filename) @@ -192,11 +213,13 @@ func (s *S3Storage) Head(token string, filename string) (contentLength uint64, e return } +// Purge cleans up the storage func (s *S3Storage) Purge(days time.Duration) (err error) { // NOOP expiration is set at upload time return nil } +// IsNotExist indicates if a file doesn't exist on storage func (s *S3Storage) IsNotExist(err error) bool { if err == nil { return false @@ -212,6 +235,7 @@ func (s *S3Storage) IsNotExist(err error) bool { return false } +// Get retrieves a file from storage func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { key := fmt.Sprintf("%s/%s", token, filename) @@ -233,6 +257,7 @@ func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, co return } +// Delete removes a file from storage func (s *S3Storage) Delete(token string, filename string) (err error) { metadata := fmt.Sprintf("%s/%s.metadata", token, filename) deleteRequest := &s3.DeleteObjectInput{ @@ -256,6 +281,7 @@ func (s *S3Storage) Delete(token string, filename string) (err error) { return } +// Put saves a file on storage func (s *S3Storage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) { key := fmt.Sprintf("%s/%s", token, filename) @@ -288,17 +314,19 @@ func (s *S3Storage) Put(token string, filename string, reader io.Reader, content return } +// GDrive is a storage backed by GDrive type GDrive struct { service *drive.Service - rootId string + rootID string basedir string localConfigPath string chunkSize int logger *log.Logger } -func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) { - b, err := ioutil.ReadFile(clientJsonFilepath) +// NewGDriveStorage is the factory for GDrive +func NewGDriveStorage(clientJSONFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) { + b, err := ioutil.ReadFile(clientJSONFilepath) if err != nil { return nil, err } @@ -315,7 +343,7 @@ func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir } chunkSize = chunkSize * 1024 * 1024 - storage := &GDrive{service: srv, basedir: basedir, rootId: "", localConfigPath: localConfigPath, chunkSize: chunkSize, logger: logger} + storage := &GDrive{service: srv, basedir: basedir, rootID: "", localConfigPath: localConfigPath, chunkSize: chunkSize, logger: logger} err = storage.setupRoot() if err != nil { return nil, err @@ -324,26 +352,26 @@ func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir return storage, nil } -const GDriveRootConfigFile = "root_id.conf" -const GDriveTokenJsonFile = "token.json" -const GDriveDirectoryMimeType = "application/vnd.google-apps.folder" +const gdriveRootConfigFile = "root_id.conf" +const gdriveTokenJSONFile = "token.json" +const gdriveDirectoryMimeType = "application/vnd.google-apps.folder" func (s *GDrive) setupRoot() error { - rootFileConfig := filepath.Join(s.localConfigPath, GDriveRootConfigFile) + rootFileConfig := filepath.Join(s.localConfigPath, gdriveRootConfigFile) - rootId, err := ioutil.ReadFile(rootFileConfig) + rootID, err := ioutil.ReadFile(rootFileConfig) if err != nil && !os.IsNotExist(err) { return err } - if string(rootId) != "" { - s.rootId = string(rootId) + if string(rootID) != "" { + s.rootID = string(rootID) return nil } dir := &drive.File{ Name: s.basedir, - MimeType: GDriveDirectoryMimeType, + MimeType: gdriveDirectoryMimeType, } di, err := s.service.Files.Create(dir).Fields("id").Do() @@ -351,8 +379,8 @@ func (s *GDrive) setupRoot() error { return err } - s.rootId = di.Id - err = ioutil.WriteFile(rootFileConfig, []byte(s.rootId), os.FileMode(0600)) + s.rootID = di.Id + err = ioutil.WriteFile(rootFileConfig, []byte(s.rootID), os.FileMode(0600)) if err != nil { return err } @@ -368,13 +396,13 @@ func (s *GDrive) list(nextPageToken string, q string) (*drive.FileList, error) { return s.service.Files.List().Fields("nextPageToken, files(id, name, mimeType)").Q(q).PageToken(nextPageToken).Do() } -func (s *GDrive) findId(filename string, token string) (string, error) { +func (s *GDrive) findID(filename string, token string) (string, error) { filename = strings.Replace(filename, `'`, `\'`, -1) filename = strings.Replace(filename, `"`, `\"`, -1) - fileId, tokenId, nextPageToken := "", "", "" + fileID, tokenID, nextPageToken := "", "", "" - q := fmt.Sprintf("'%s' in parents and name='%s' and mimeType='%s' and trashed=false", s.rootId, token, GDriveDirectoryMimeType) + q := fmt.Sprintf("'%s' in parents and name='%s' and mimeType='%s' and trashed=false", s.rootID, token, gdriveDirectoryMimeType) l, err := s.list(nextPageToken, q) if err != nil { return "", err @@ -382,7 +410,7 @@ func (s *GDrive) findId(filename string, token string) (string, error) { for 0 < len(l.Files) { for _, fi := range l.Files { - tokenId = fi.Id + tokenID = fi.Id break } @@ -391,15 +419,18 @@ func (s *GDrive) findId(filename string, token string) (string, error) { } l, err = s.list(l.NextPageToken, q) + if err != nil { + return "", err + } } if filename == "" { - return tokenId, nil - } else if tokenId == "" { + return tokenID, nil + } else if tokenID == "" { return "", fmt.Errorf("Cannot find file %s/%s", token, filename) } - q = fmt.Sprintf("'%s' in parents and name='%s' and mimeType!='%s' and trashed=false", tokenId, filename, GDriveDirectoryMimeType) + q = fmt.Sprintf("'%s' in parents and name='%s' and mimeType!='%s' and trashed=false", tokenID, filename, gdriveDirectoryMimeType) l, err = s.list(nextPageToken, q) if err != nil { return "", err @@ -408,7 +439,7 @@ func (s *GDrive) findId(filename string, token string) (string, error) { for 0 < len(l.Files) { for _, fi := range l.Files { - fileId = fi.Id + fileID = fi.Id break } @@ -417,28 +448,33 @@ func (s *GDrive) findId(filename string, token string) (string, error) { } l, err = s.list(l.NextPageToken, q) + if err != nil { + return "", err + } } - if fileId == "" { + if fileID == "" { return "", fmt.Errorf("Cannot find file %s/%s", token, filename) } - return fileId, nil + return fileID, nil } +// Type returns the storage type func (s *GDrive) Type() string { return "gdrive" } +// Head retrieves content length of a file from storage func (s *GDrive) Head(token string, filename string) (contentLength uint64, err error) { - var fileId string - fileId, err = s.findId(filename, token) + var fileID string + fileID, err = s.findID(filename, token) if err != nil { return } var fi *drive.File - if fi, err = s.service.Files.Get(fileId).Fields("size").Do(); err != nil { + if fi, err = s.service.Files.Get(fileID).Fields("size").Do(); err != nil { return } @@ -447,15 +483,16 @@ func (s *GDrive) Head(token string, filename string) (contentLength uint64, err return } +// Get retrieves a file from storage func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { - var fileId string - fileId, err = s.findId(filename, token) + var fileID string + fileID, err = s.findID(filename, token) if err != nil { return } var fi *drive.File - fi, err = s.service.Files.Get(fileId).Fields("size", "md5Checksum").Do() + 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 @@ -465,7 +502,7 @@ func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, conte ctx := context.Background() var res *http.Response - res, err = s.service.Files.Get(fileId).Context(ctx).Download() + res, err = s.service.Files.Get(fileID).Context(ctx).Download() if err != nil { return } @@ -475,25 +512,27 @@ func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, conte return } +// Delete removes a file from storage func (s *GDrive) Delete(token string, filename string) (err error) { - metadata, _ := s.findId(fmt.Sprintf("%s.metadata", filename), token) + metadata, _ := s.findID(fmt.Sprintf("%s.metadata", filename), token) s.service.Files.Delete(metadata).Do() - var fileId string - fileId, err = s.findId(filename, token) + var fileID string + fileID, err = s.findID(filename, token) if err != nil { return } - err = s.service.Files.Delete(fileId).Do() + err = s.service.Files.Delete(fileID).Do() return } +// Purge cleans up the storage func (s *GDrive) Purge(days time.Duration) (err error) { nextPageToken := "" expirationDate := time.Now().Add(-1 * days).Format(time.RFC3339) - q := fmt.Sprintf("'%s' in parents and modifiedTime < '%s' and mimeType!='%s' and trashed=false", s.rootId, expirationDate, GDriveDirectoryMimeType) + q := fmt.Sprintf("'%s' in parents and modifiedTime < '%s' and mimeType!='%s' and trashed=false", s.rootID, expirationDate, gdriveDirectoryMimeType) l, err := s.list(nextPageToken, q) if err != nil { return err @@ -512,32 +551,39 @@ func (s *GDrive) Purge(days time.Duration) (err error) { } l, err = s.list(l.NextPageToken, q) + if err != nil { + return + } } return } +// IsNotExist indicates if a file doesn't exist on storage func (s *GDrive) IsNotExist(err error) bool { - if err != nil { - if e, ok := err.(*googleapi.Error); ok { - return e.Code == http.StatusNotFound - } + if err == nil { + return false + } + + if e, ok := err.(*googleapi.Error); ok { + return e.Code == http.StatusNotFound } return false } +// Put saves a file on storage func (s *GDrive) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error { - dirId, err := s.findId("", token) + dirID, err := s.findID("", token) if err != nil { return err } - if dirId == "" { + if dirID == "" { dir := &drive.File{ Name: token, - Parents: []string{s.rootId}, - MimeType: GDriveDirectoryMimeType, + Parents: []string{s.rootID}, + MimeType: gdriveDirectoryMimeType, } di, err := s.service.Files.Create(dir).Fields("id").Do() @@ -545,13 +591,13 @@ func (s *GDrive) Put(token string, filename string, reader io.Reader, contentTyp return err } - dirId = di.Id + dirID = di.Id } // Instantiate empty drive file dst := &drive.File{ Name: filename, - Parents: []string{dirId}, + Parents: []string{dirID}, MimeType: contentType, } @@ -567,7 +613,7 @@ func (s *GDrive) Put(token string, filename string, reader io.Reader, contentTyp // 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) + tokenFile := filepath.Join(localConfigPath, gdriveTokenJSONFile) tok, err := gDriveTokenFromFile(tokenFile) if err != nil { tok = getGDriveTokenFromWeb(config, logger) @@ -619,6 +665,7 @@ func saveGDriveToken(path string, token *oauth2.Token, logger *log.Logger) { json.NewEncoder(f).Encode(token) } +// StorjStorage is a storage backed by Storj type StorjStorage struct { Storage project *uplink.Project @@ -627,6 +674,7 @@ type StorjStorage struct { logger *log.Logger } +// NewStorjStorage is the factory for StorjStorage func NewStorjStorage(access, bucket string, purgeDays int, logger *log.Logger) (*StorjStorage, error) { var instance StorjStorage var err error @@ -657,10 +705,12 @@ func NewStorjStorage(access, bucket string, purgeDays int, logger *log.Logger) ( return &instance, nil } +// Type returns the storage type func (s *StorjStorage) Type() string { return "storj" } +// Head retrieves content length of a file from storage func (s *StorjStorage) Head(token string, filename string) (contentLength uint64, err error) { key := storj.JoinPaths(token, filename) @@ -676,6 +726,7 @@ func (s *StorjStorage) Head(token string, filename string) (contentLength uint64 return } +// Get retrieves a file from storage func (s *StorjStorage) Get(token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { key := storj.JoinPaths(token, filename) @@ -694,6 +745,7 @@ func (s *StorjStorage) Get(token string, filename string) (reader io.ReadCloser, return } +// Delete removes a file from storage func (s *StorjStorage) Delete(token string, filename string) (err error) { key := storj.JoinPaths(token, filename) @@ -706,11 +758,13 @@ func (s *StorjStorage) Delete(token string, filename string) (err error) { return } +// Purge cleans up the storage func (s *StorjStorage) Purge(days time.Duration) (err error) { // NOOP expiration is set at upload time return nil } +// Put saves a file on storage func (s *StorjStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) { key := storj.JoinPaths(token, filename) @@ -745,6 +799,7 @@ func (s *StorjStorage) Put(token string, filename string, reader io.Reader, cont return err } +// IsNotExist indicates if a file doesn't exist on storage func (s *StorjStorage) IsNotExist(err error) bool { return errors.Is(err, uplink.ErrObjectNotFound) } diff --git a/server/token.go b/server/token.go index b5f0247b..f3aa012e 100644 --- a/server/token.go +++ b/server/token.go @@ -29,12 +29,12 @@ import ( ) const ( - // characters used for short-urls + // SYMBOLS characters used for short-urls SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ) // generate a token -func Token(length int) string { +func token(length int) string { result := "" for i := 0; i < length; i++ { x := rand.Intn(len(SYMBOLS) - 1) diff --git a/server/token_test.go b/server/token_test.go index ae9afa0f..cec3d793 100644 --- a/server/token_test.go +++ b/server/token_test.go @@ -4,12 +4,12 @@ import "testing" func BenchmarkTokenConcat(b *testing.B) { for i := 0; i < b.N; i++ { - _ = Token(5) + Token(5) + _ = token(5) + token(5) } } func BenchmarkTokenLonger(b *testing.B) { for i := 0; i < b.N; i++ { - _ = Token(10) + _ = token(10) } } diff --git a/server/utils.go b/server/utils.go index 3a53adc6..e97ff1a5 100644 --- a/server/utils.go +++ b/server/utils.go @@ -50,7 +50,7 @@ func getAwsSession(accessKey, secretKey, region, endpoint string, forcePathStyle } func formatNumber(format string, s uint64) string { - return RenderFloat(format, float64(s)) + return renderFloat(format, float64(s)) } var renderFloatPrecisionMultipliers = [10]float64{ @@ -79,7 +79,7 @@ var renderFloatPrecisionRounders = [10]float64{ 0.0000000005, } -func RenderFloat(format string, n float64) string { +func renderFloat(format string, n float64) string { // Special cases: // NaN = "NaN" // +Inf = "+Infinity" @@ -127,7 +127,7 @@ func RenderFloat(format string, n float64) string { // +0000 if formatDirectiveIndices[0] == 0 { if formatDirectiveChars[formatDirectiveIndices[0]] != '+' { - panic("RenderFloat(): invalid positive sign directive") + panic("renderFloat(): invalid positive sign directive") } positiveStr = "+" formatDirectiveIndices = formatDirectiveIndices[1:] @@ -141,7 +141,7 @@ func RenderFloat(format string, n float64) string { // 000,000.00 if len(formatDirectiveIndices) == 2 { if (formatDirectiveIndices[1] - formatDirectiveIndices[0]) != 4 { - panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") + panic("renderFloat(): thousands separator directive must be followed by 3 digit-specifiers") } thousandStr = string(formatDirectiveChars[formatDirectiveIndices[0]]) formatDirectiveIndices = formatDirectiveIndices[1:] @@ -201,8 +201,8 @@ func RenderFloat(format string, n float64) string { return signStr + intStr + decimalStr + fracStr } -func RenderInteger(format string, n int) string { - return RenderFloat(format, float64(n)) +func renderInteger(format string, n int) string { + return renderFloat(format, float64(n)) } // Request.RemoteAddress contains port, which we want to remove i.e.: diff --git a/server/virustotal.go b/server/virustotal.go index 3e0f6189..cef80dd5 100644 --- a/server/virustotal.go +++ b/server/virustotal.go @@ -29,7 +29,6 @@ import ( "io" "net/http" - _ "github.com/PuerkitoBio/ghost/handlers" "github.com/gorilla/mux" virustotal "github.com/dutchcoders/go-virustotal"