diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d9419f5..12ddd7c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,3 +29,17 @@ jobs: go version go vet ./... go test ./... + golangci: + name: Linting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v1 + with: + go-version: 1.17 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + version: latest + skip-go-installation: true + args: "--config .golangci.yml" diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..6b15d9c --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,20 @@ +run: + deadline: 10m + issues-exit-code: 1 + tests: true + +output: + format: colored-line-number + print-issued-lines: true + print-linter-name: true + +linters: + disable: + - deadcode + - unused + +issues: + max-issues-per-linter: 0 + max-same-issues: 0 + new: false + exclude-use-default: false diff --git a/main.go b/main.go index 54d7259..924a4cf 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,16 @@ package main -import "github.com/dutchcoders/transfer.sh/cmd" +import ( + "log" + "os" + + "github.com/dutchcoders/transfer.sh/cmd" +) func main() { app := cmd.New() - app.RunAndExitOnError() + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } diff --git a/server/handlers.go b/server/handlers.go index 64a2432..9a9b7ba 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -94,7 +94,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.")) } func canContainsXSS(contentType string) bool { @@ -389,7 +389,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, s.proxyPort).ResolveReference(relativeURL).String()) + _, _ = w.Write([]byte(getURL(r, s.proxyPort).ResolveReference(relativeURL).String())) } } } @@ -454,7 +454,7 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { contentLength := r.ContentLength - defer r.Body.Close() + defer CloseCheck(r.Body.Close) file, err := ioutil.TempFile(s.tempPath, "transfer-") defer s.cleanTmpFile(file) @@ -552,7 +552,7 @@ func (s *Server) putHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Url-Delete", resolveURL(r, deleteURL, s.proxyPort)) - fmt.Fprint(w, resolveURL(r, relativeURL, s.proxyPort)) + _, _ = w.Write([]byte(resolveURL(r, relativeURL, s.proxyPort))) } func resolveURL(r *http.Request, u *url.URL, proxyPort string) string { @@ -562,13 +562,9 @@ func resolveURL(r *http.Request, u *url.URL, proxyPort string) string { } func resolveKey(key, proxyPath string) string { - if strings.HasPrefix(key, "/") { - key = key[1:] - } + key = strings.TrimPrefix(key, "/") - if strings.HasPrefix(key, proxyPath) { - key = key[len(proxyPath):] - } + key = strings.TrimPrefix(key, proxyPath) key = strings.Replace(key, "\\", "/", -1) @@ -576,18 +572,18 @@ func resolveKey(key, proxyPath string) string { } func resolveWebAddress(r *http.Request, proxyPath string, proxyPort string) string { - url := getURL(r, proxyPort) + rUrl := getURL(r, proxyPort) var webAddress string if len(proxyPath) == 0 { webAddress = fmt.Sprintf("%s://%s/", - url.ResolveReference(url).Scheme, - url.ResolveReference(url).Host) + rUrl.ResolveReference(rUrl).Scheme, + rUrl.ResolveReference(rUrl).Host) } else { webAddress = fmt.Sprintf("%s://%s/%s", - url.ResolveReference(url).Scheme, - url.ResolveReference(url).Host, + rUrl.ResolveReference(rUrl).Scheme, + rUrl.ResolveReference(rUrl).Host, proxyPath) } @@ -647,7 +643,7 @@ func (metadata metadata) remainingLimitHeaderValues() (remainingDownloads, remai if metadata.MaxDate.IsZero() { remainingDays = "n/a" } else { - timeDifference := metadata.MaxDate.Sub(time.Now()) + timeDifference := time.Until(metadata.MaxDate) remainingDays = strconv.Itoa(int(timeDifference.Hours()/24) + 1) } @@ -666,8 +662,6 @@ func (s *Server) lock(token, filename string) { lock, _ := s.locks.LoadOrStore(key, &sync.Mutex{}) lock.(*sync.Mutex).Lock() - - return } func (s *Server) unlock(token, filename string) { @@ -685,12 +679,12 @@ func (s *Server) checkMetadata(token, filename string, increaseDownload bool) (m var metadata metadata r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) + defer CloseCheck(r.Close) + 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 { @@ -721,14 +715,14 @@ func (s *Server) checkDeletionToken(deletionToken, token, filename string) error var metadata metadata r, _, err := s.storage.Get(token, fmt.Sprintf("%s.metadata", filename)) + defer CloseCheck(r.Close) + if s.storage.IsNotExist(err) { return errors.New("metadata doesn't exist") } 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 { @@ -742,9 +736,9 @@ func (s *Server) purgeHandler() { ticker := time.NewTicker(s.purgeInterval) go func() { for { - select { - case <-ticker.C: - err := s.storage.Purge(s.purgeDays) + <-ticker.C + err := s.storage.Purge(s.purgeDays) + if err != nil { s.logger.Printf("error cleaning up expired files: %v", err) } } @@ -800,6 +794,7 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) { } reader, _, err := s.storage.Get(token, filename) + defer CloseCheck(reader.Close) if err != nil { if s.storage.IsNotExist(err) { @@ -812,13 +807,11 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) { return } - defer reader.Close() - header := &zip.FileHeader{ - Name: strings.Split(key, "/")[1], - Method: zip.Store, - ModifiedTime: uint16(time.Now().UnixNano()), - ModifiedDate: uint16(time.Now().UnixNano()), + Name: strings.Split(key, "/")[1], + Method: zip.Store, + + Modified: time.Now().UTC(), } fw, err := zw.CreateHeader(header) @@ -854,11 +847,11 @@ 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() + gw := gzip.NewWriter(w) + defer CloseCheck(gw.Close) - zw := tar.NewWriter(os) - defer zw.Close() + zw := tar.NewWriter(gw) + defer CloseCheck(zw.Close) for _, key := range strings.Split(files, ",") { key = resolveKey(key, s.proxyPath) @@ -872,6 +865,8 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) { } reader, contentLength, err := s.storage.Get(token, filename) + defer CloseCheck(reader.Close) + if err != nil { if s.storage.IsNotExist(err) { http.Error(w, "File not found", 404) @@ -883,8 +878,6 @@ func (s *Server) tarGzHandler(w http.ResponseWriter, r *http.Request) { return } - defer reader.Close() - header := &tar.Header{ Name: strings.Split(key, "/")[1], Size: int64(contentLength), @@ -917,7 +910,7 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Connection", "close") zw := tar.NewWriter(w) - defer zw.Close() + defer CloseCheck(zw.Close) for _, key := range strings.Split(files, ",") { key = resolveKey(key, s.proxyPath) @@ -931,6 +924,8 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) { } reader, contentLength, err := s.storage.Get(token, filename) + defer CloseCheck(reader.Close) + if err != nil { if s.storage.IsNotExist(err) { http.Error(w, "File not found", 404) @@ -942,8 +937,6 @@ func (s *Server) tarHandler(w http.ResponseWriter, r *http.Request) { return } - defer reader.Close() - header := &tar.Header{ Name: strings.Split(key, "/")[1], Size: int64(contentLength), @@ -1015,6 +1008,8 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) { contentType := metadata.ContentType reader, contentLength, err := s.storage.Get(token, filename) + defer CloseCheck(reader.Close) + if s.storage.IsNotExist(err) { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return @@ -1024,8 +1019,6 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) { return } - defer reader.Close() - var disposition string if action == "inline" { @@ -1049,14 +1042,14 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) { if w.Header().Get("Range") != "" || strings.HasPrefix(metadata.ContentType, "video") || strings.HasPrefix(metadata.ContentType, "audio") { file, err := ioutil.TempFile(s.tempPath, "range-") + defer s.cleanTmpFile(file) + if err != nil { s.logger.Printf("%s", err.Error()) http.Error(w, "Error occurred copying to output stream", http.StatusInternalServerError) return } - defer s.cleanTmpFile(file) - _, err = io.Copy(file, reader) if err != nil { s.logger.Printf("%s", err.Error()) @@ -1073,8 +1066,6 @@ func (s *Server) getHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "Error occurred copying to output stream", http.StatusInternalServerError) return } - - return } // RedirectHandler handles redirect @@ -1129,7 +1120,6 @@ func ipFilterHandler(h http.Handler, ipFilterOptions *IPFilterOptions) http.Hand } else { WrapIPFilter(h, *ipFilterOptions).ServeHTTP(w, r) } - return } } @@ -1143,13 +1133,13 @@ func (s *Server) basicAuthHandler(h http.Handler) http.HandlerFunc { w.Header().Set("WWW-Authenticate", "Basic realm=\"Restricted\"") username, password, authOK := r.BasicAuth() - if authOK == false { - http.Error(w, "Not authorized", 401) + if !authOK { + http.Error(w, "Not authorized", http.StatusUnauthorized) return } if username != s.AuthUser || password != s.AuthPass { - http.Error(w, "Not authorized", 401) + http.Error(w, "Not authorized", http.StatusUnauthorized) return } diff --git a/server/server.go b/server/server.go index b77537d..88af636 100644 --- a/server/server.go +++ b/server/server.go @@ -401,7 +401,7 @@ func (s *Server) Run() { go func() { s.logger.Println("Profiled listening at: :6060") - http.ListenAndServe(":6060", nil) + _ = http.ListenAndServe(":6060", nil) }() } @@ -432,8 +432,14 @@ func (s *Server) Run() { s.logger.Panicf("Unable to parse: path=%s, err=%s", path, err) } - htmlTemplates.New(stripPrefix(path)).Parse(string(bytes)) - textTemplates.New(stripPrefix(path)).Parse(string(bytes)) + _, err = htmlTemplates.New(stripPrefix(path)).Parse(string(bytes)) + if err != nil { + s.logger.Panicln("Unable to parse template") + } + _, err = textTemplates.New(stripPrefix(path)).Parse(string(bytes)) + if err != nil { + s.logger.Panicln("Unable to parse template") + } } } @@ -501,7 +507,7 @@ func (s *Server) Run() { r.NotFoundHandler = http.HandlerFunc(s.notFoundHandler) - mime.AddExtensionType(".md", "text/x-markdown") + _ = mime.AddExtensionType(".md", "text/x-markdown") s.logger.Printf("Transfer.sh server started.\nusing temp folder: %s\nusing storage provider: %s", s.tempPath, s.storage.Type()) @@ -540,7 +546,7 @@ func (s *Server) Run() { s.logger.Printf("listening on port: %v\n", s.ListenerString) go func() { - srvr.ListenAndServe() + _ = srvr.ListenAndServe() }() } diff --git a/server/storage.go b/server/storage.go index 3daf829..5447d11 100644 --- a/server/storage.go +++ b/server/storage.go @@ -99,7 +99,7 @@ func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, // 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) + _ = os.Remove(metadata) path := filepath.Join(s.basedir, token, filename) err = os.Remove(path) @@ -148,12 +148,13 @@ func (s *LocalStorage) Put(token string, filename string, reader io.Reader, cont return err } - if f, err = os.OpenFile(filepath.Join(path, filename), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600); err != nil { + f, err = os.OpenFile(filepath.Join(path, filename), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + defer CloseCheck(f.Close) + + if err != nil { return err } - defer f.Close() - if _, err = io.Copy(f, reader); err != nil { return err } @@ -337,7 +338,8 @@ func NewGDriveStorage(clientJSONFilepath string, localConfigPath string, basedir return nil, err } - srv, err := drive.New(getGDriveClient(config, localConfigPath, logger)) + // ToDo: Upgrade deprecated version + srv, err := drive.New(getGDriveClient(config, localConfigPath, logger)) // nolint: staticcheck if err != nil { return nil, err } @@ -427,7 +429,7 @@ func (s *GDrive) findID(filename string, token string) (string, error) { if filename == "" { return tokenID, nil } else if tokenID == "" { - return "", fmt.Errorf("Cannot find file %s/%s", token, filename) + 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) @@ -454,7 +456,7 @@ func (s *GDrive) findID(filename string, token string) (string, error) { } if fileID == "" { - return "", fmt.Errorf("Cannot find file %s/%s", token, filename) + return "", fmt.Errorf("cannot find file %s/%s", token, filename) } return fileID, nil @@ -493,8 +495,11 @@ func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, conte var fi *drive.File fi, err = s.service.Files.Get(fileID).Fields("size", "md5Checksum").Do() + if err != nil { + return + } if !s.hasChecksum(fi) { - err = fmt.Errorf("Cannot find file %s/%s", token, filename) + err = fmt.Errorf("cannot find file %s/%s", token, filename) return } @@ -515,7 +520,7 @@ func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, conte // 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) - s.service.Files.Delete(metadata).Do() + _ = s.service.Files.Delete(metadata).Do() var fileID string fileID, err = s.findID(filename, token) @@ -584,6 +589,7 @@ func (s *GDrive) Put(token string, filename string, reader io.Reader, contentTyp Name: token, Parents: []string{s.rootID}, MimeType: gdriveDirectoryMimeType, + Size: int64(contentLength), } di, err := s.service.Files.Create(dir).Fields("id").Do() @@ -644,7 +650,7 @@ func getGDriveTokenFromWeb(config *oauth2.Config, logger *log.Logger) *oauth2.To // Retrieves a token from a local file. func gDriveTokenFromFile(file string) (*oauth2.Token, error) { f, err := os.Open(file) - defer f.Close() + defer CloseCheck(f.Close) if err != nil { return nil, err } @@ -657,12 +663,15 @@ func gDriveTokenFromFile(file string) (*oauth2.Token, error) { func saveGDriveToken(path string, token *oauth2.Token, logger *log.Logger) { logger.Printf("Saving credential file to: %s\n", path) f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) - defer f.Close() + defer CloseCheck(f.Close) if err != nil { logger.Fatalf("Unable to cache oauth token: %v", err) } - json.NewEncoder(f).Encode(token) + err = json.NewEncoder(f).Encode(token) + if err != nil { + logger.Fatalf("Unable to encode oauth token: %v", err) + } } // StorjStorage is a storage backed by Storj diff --git a/server/utils.go b/server/utils.go index e97ff1a..3c84cbe 100644 --- a/server/utils.go +++ b/server/utils.go @@ -279,3 +279,9 @@ func formatSize(size int64) string { getSuffix := suffixes[int(math.Floor(base))] return fmt.Sprintf("%s %s", strconv.FormatFloat(newVal, 'f', -1, 64), getSuffix) } + +func CloseCheck(f func() error) { + if err := f(); err != nil { + fmt.Println("Received close error:", err) + } +} diff --git a/server/virustotal.go b/server/virustotal.go index cef80dd..24fa8e7 100644 --- a/server/virustotal.go +++ b/server/virustotal.go @@ -26,7 +26,6 @@ package server import ( "fmt" - "io" "net/http" "github.com/gorilla/mux" @@ -49,9 +48,7 @@ func (s *Server) virusTotalHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), 500) } - var reader io.Reader - - reader = r.Body + reader := r.Body result, err := vt.Scan(filename, reader) if err != nil { @@ -59,5 +56,5 @@ func (s *Server) virusTotalHandler(w http.ResponseWriter, r *http.Request) { } s.logger.Println(result) - w.Write([]byte(fmt.Sprintf("%v\n", result.Permalink))) + _, _ = w.Write([]byte(fmt.Sprintf("%v\n", result.Permalink))) }