all: update gdrive client and various linting cleanups (#497)

This commit is contained in:
Stefan Benten
2022-07-14 18:02:18 +02:00
committed by GitHub
parent 21812d3efc
commit 64c7759126
11 changed files with 72 additions and 99 deletions

View File

@@ -300,7 +300,7 @@ type Cmd struct {
*cli.App *cli.App
} }
func versionCommand(ctx *cli.Context) { func versionCommand(_ *cli.Context) {
fmt.Println(color.YellowString("transfer.sh %s: Easy file sharing from the command line", Version)) fmt.Println(color.YellowString("transfer.sh %s: Easy file sharing from the command line", Version))
} }
@@ -470,7 +470,7 @@ func New() *Cmd {
options = append(options, server.UseStorage(store)) options = append(options, server.UseStorage(store))
} }
case "gdrive": case "gdrive":
chunkSize := c.Int("gdrive-chunk-size") chunkSize := c.Int("gdrive-chunk-size") * 1024 * 1024
if clientJSONFilepath := c.String("gdrive-client-json-filepath"); clientJSONFilepath == "" { if clientJSONFilepath := c.String("gdrive-client-json-filepath"); clientJSONFilepath == "" {
panic("client-json-filepath not set.") panic("client-json-filepath not set.")

View File

@@ -34,7 +34,7 @@ import (
"net/http" "net/http"
"time" "time"
clamd "github.com/dutchcoders/go-clamd" "github.com/dutchcoders/go-clamd"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )

View File

@@ -38,7 +38,7 @@ import (
"fmt" "fmt"
"github.com/dutchcoders/transfer.sh/server/storage" "github.com/dutchcoders/transfer.sh/server/storage"
"html" "html"
html_template "html/template" htmlTemplate "html/template"
"io" "io"
"io/ioutil" "io/ioutil"
"mime" "mime"
@@ -51,13 +51,13 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
text_template "text/template" textTemplate "text/template"
"time" "time"
web "github.com/dutchcoders/transfer.sh-web" web "github.com/dutchcoders/transfer.sh-web"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
blackfriday "github.com/russross/blackfriday/v2" "github.com/russross/blackfriday/v2"
"github.com/skip2/go-qrcode" "github.com/skip2/go-qrcode"
"golang.org/x/net/idna" "golang.org/x/net/idna"
) )
@@ -73,24 +73,24 @@ func stripPrefix(path string) string {
return strings.Replace(path, web.Prefix+"/", "", -1) return strings.Replace(path, web.Prefix+"/", "", -1)
} }
func initTextTemplates() *text_template.Template { func initTextTemplates() *textTemplate.Template {
templateMap := text_template.FuncMap{"format": formatNumber} templateMap := textTemplate.FuncMap{"format": formatNumber}
// Templates with functions available to them // Templates with functions available to them
var templates = text_template.New("").Funcs(templateMap) var templates = textTemplate.New("").Funcs(templateMap)
return templates return templates
} }
func initHTMLTemplates() *html_template.Template { func initHTMLTemplates() *htmlTemplate.Template {
templateMap := html_template.FuncMap{"format": formatNumber} templateMap := htmlTemplate.FuncMap{"format": formatNumber}
// Templates with functions available to them // Templates with functions available to them
var templates = html_template.New("").Funcs(templateMap) var templates = htmlTemplate.New("").Funcs(templateMap)
return templates return templates
} }
func healthHandler(w http.ResponseWriter, r *http.Request) { func healthHandler(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("Approaching Neutral Zone, all systems normal and functioning.")) _, _ = w.Write([]byte("Approaching Neutral Zone, all systems normal and functioning."))
} }
@@ -138,7 +138,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) {
} }
var templatePath string var templatePath string
var content html_template.HTML var content htmlTemplate.HTML
switch { switch {
case strings.HasPrefix(contentType, "image/"): case strings.HasPrefix(contentType, "image/"):
@@ -166,9 +166,9 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(contentType, "text/x-markdown") || strings.HasPrefix(contentType, "text/markdown") { if strings.HasPrefix(contentType, "text/x-markdown") || strings.HasPrefix(contentType, "text/markdown") {
unsafe := blackfriday.Run(data) unsafe := blackfriday.Run(data)
output := bluemonday.UGCPolicy().SanitizeBytes(unsafe) output := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
content = html_template.HTML(output) content = htmlTemplate.HTML(output)
} else if strings.HasPrefix(contentType, "text/plain") { } else if strings.HasPrefix(contentType, "text/plain") {
content = html_template.HTML(fmt.Sprintf("<pre>%s</pre>", html.EscapeString(string(data)))) content = htmlTemplate.HTML(fmt.Sprintf("<pre>%s</pre>", html.EscapeString(string(data))))
} else { } else {
templatePath = "download.sandbox.html" templatePath = "download.sandbox.html"
} }
@@ -195,7 +195,7 @@ func (s *Server) previewHandler(w http.ResponseWriter, r *http.Request) {
data := struct { data := struct {
ContentType string ContentType string
Content html_template.HTML Content htmlTemplate.HTML
Filename string Filename string
URL string URL string
URLGet string URLGet string
@@ -282,7 +282,7 @@ func (s *Server) viewHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
func (s *Server) notFoundHandler(w http.ResponseWriter, r *http.Request) { func (s *Server) notFoundHandler(w http.ResponseWriter, _ *http.Request) {
http.Error(w, http.StatusText(404), 404) http.Error(w, http.StatusText(404), 404)
} }
@@ -303,15 +303,15 @@ func (s *Server) postHandler(w http.ResponseWriter, r *http.Request) {
responseBody := "" responseBody := ""
for _, fheaders := range r.MultipartForm.File { for _, fHeaders := range r.MultipartForm.File {
for _, fheader := range fheaders { for _, fHeader := range fHeaders {
filename := sanitize(fheader.Filename) filename := sanitize(fHeader.Filename)
contentType := mime.TypeByExtension(filepath.Ext(fheader.Filename)) contentType := mime.TypeByExtension(filepath.Ext(fHeader.Filename))
var f io.Reader var f io.Reader
var err error var err error
if f, err = fheader.Open(); err != nil { if f, err = fHeader.Open(); err != nil {
s.logger.Printf("%s", err.Error()) s.logger.Printf("%s", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return

View File

@@ -88,10 +88,10 @@ func (f *ipFilter) BlockIP(ip string) bool {
} }
func (f *ipFilter) ToggleIP(str string, allowed bool) bool { func (f *ipFilter) ToggleIP(str string, allowed bool) bool {
//check if has subnet //check if provided string describes a subnet
if ip, net, err := net.ParseCIDR(str); err == nil { if ip, network, err := net.ParseCIDR(str); err == nil {
// containing only one ip? // containing only one ip?
if n, total := net.Mask.Size(); n == total { if n, total := network.Mask.Size(); n == total {
f.mut.Lock() f.mut.Lock()
f.ips[ip.String()] = allowed f.ips[ip.String()] = allowed
f.mut.Unlock() f.mut.Unlock()
@@ -110,7 +110,7 @@ func (f *ipFilter) ToggleIP(str string, allowed bool) bool {
if !found { if !found {
f.subnets = append(f.subnets, &subnet{ f.subnets = append(f.subnets, &subnet{
str: str, str: str,
ipnet: net, ipnet: network,
allowed: allowed, allowed: allowed,
}) })
} }
@@ -182,7 +182,7 @@ func (f *ipFilter) NetBlocked(ip net.IP) bool {
return !f.NetAllowed(ip) return !f.NetAllowed(ip)
} }
//WrapIPFilter the provided handler with simple IP blocking middleware //Wrap the provided handler with simple IP blocking middleware
//using this IP filter and its configuration //using this IP filter and its configuration
func (f *ipFilter) Wrap(next http.Handler) http.Handler { func (f *ipFilter) Wrap(next http.Handler) http.Handler {
return &ipFilterMiddleware{ipFilter: f, next: next} return &ipFilterMiddleware{ipFilter: f, next: next}

View File

@@ -26,7 +26,7 @@ package server
import ( import (
"context" "context"
crypto_rand "crypto/rand" cryptoRand "crypto/rand"
"crypto/tls" "crypto/tls"
"encoding/binary" "encoding/binary"
"errors" "errors"
@@ -381,7 +381,7 @@ func New(options ...OptionFn) (*Server, error) {
func init() { func init() {
var seedBytes [8]byte var seedBytes [8]byte
if _, err := crypto_rand.Read(seedBytes[:]); err != nil { if _, err := cryptoRand.Read(seedBytes[:]); err != nil {
panic("cannot obtain cryptographically secure seed") panic("cannot obtain cryptographically secure seed")
} }
rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:]))) rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:])))
@@ -475,7 +475,7 @@ func (s *Server) Run() {
return false return false
} }
match = (r.Referer() == "") match = r.Referer() == ""
u, err := url.Parse(r.Referer()) u, err := url.Parse(r.Referer())
if err != nil { if err != nil {

View File

@@ -15,8 +15,9 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/google" "golang.org/x/oauth2/google"
drive "google.golang.org/api/drive/v3" "google.golang.org/api/drive/v3"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
"google.golang.org/api/option"
) )
// GDrive is a storage backed by GDrive // GDrive is a storage backed by GDrive
@@ -29,8 +30,15 @@ type GDrive struct {
logger *log.Logger logger *log.Logger
} }
const gDriveRootConfigFile = "root_id.conf"
const gDriveTokenJSONFile = "token.json"
const gDriveDirectoryMimeType = "application/vnd.google-apps.folder"
// NewGDriveStorage is the factory for GDrive // NewGDriveStorage is the factory for GDrive
func NewGDriveStorage(clientJSONFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) { func NewGDriveStorage(clientJSONFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) {
ctx := context.TODO()
b, err := ioutil.ReadFile(clientJSONFilepath) b, err := ioutil.ReadFile(clientJSONFilepath)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -42,13 +50,13 @@ func NewGDriveStorage(clientJSONFilepath string, localConfigPath string, basedir
return nil, err return nil, err
} }
// ToDo: Upgrade deprecated version httpClient := getGDriveClient(ctx, config, localConfigPath, logger)
srv, err := drive.New(getGDriveClient(context.TODO(), config, localConfigPath, logger)) // nolint: staticcheck
srv, err := drive.NewService(ctx, option.WithHTTPClient(httpClient))
if err != nil { if err != nil {
return nil, err return nil, err
} }
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() err = storage.setupRoot()
if err != nil { if err != nil {
@@ -58,12 +66,8 @@ func NewGDriveStorage(clientJSONFilepath string, localConfigPath string, basedir
return storage, nil return storage, nil
} }
const gdriveRootConfigFile = "root_id.conf"
const gdriveTokenJSONFile = "token.json"
const gdriveDirectoryMimeType = "application/vnd.google-apps.folder"
func (s *GDrive) setupRoot() error { 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) { if err != nil && !os.IsNotExist(err) {
@@ -77,7 +81,7 @@ func (s *GDrive) setupRoot() error {
dir := &drive.File{ dir := &drive.File{
Name: s.basedir, Name: s.basedir,
MimeType: gdriveDirectoryMimeType, MimeType: gDriveDirectoryMimeType,
} }
di, err := s.service.Files.Create(dir).Fields("id").Do() di, err := s.service.Files.Create(dir).Fields("id").Do()
@@ -108,7 +112,7 @@ func (s *GDrive) findID(filename string, token string) (string, error) {
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) l, err := s.list(nextPageToken, q)
if err != nil { if err != nil {
return "", err return "", err
@@ -136,7 +140,7 @@ func (s *GDrive) findID(filename string, token string) (string, error) {
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) q = fmt.Sprintf("'%s' in parents and name='%s' and mimeType!='%s' and trashed=false", tokenID, filename, gDriveDirectoryMimeType)
l, err = s.list(nextPageToken, q) l, err = s.list(nextPageToken, q)
if err != nil { if err != nil {
return "", err return "", err
@@ -180,7 +184,7 @@ func (s *GDrive) Head(ctx context.Context, token string, filename string) (conte
} }
var fi *drive.File 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).Context(ctx).Fields("size").Do(); err != nil {
return return
} }
@@ -231,7 +235,7 @@ func (s *GDrive) Delete(ctx context.Context, token string, filename string) (err
return return
} }
err = s.service.Files.Delete(fileID).Do() err = s.service.Files.Delete(fileID).Context(ctx).Do()
return return
} }
@@ -240,7 +244,7 @@ func (s *GDrive) Purge(ctx context.Context, days time.Duration) (err error) {
nextPageToken := "" nextPageToken := ""
expirationDate := time.Now().Add(-1 * days).Format(time.RFC3339) 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) l, err := s.list(nextPageToken, q)
if err != nil { if err != nil {
return err return err
@@ -248,7 +252,7 @@ func (s *GDrive) Purge(ctx context.Context, days time.Duration) (err error) {
for 0 < len(l.Files) { for 0 < len(l.Files) {
for _, fi := range l.Files { for _, fi := range l.Files {
err = s.service.Files.Delete(fi.Id).Do() err = s.service.Files.Delete(fi.Id).Context(ctx).Do()
if err != nil { if err != nil {
return return
} }
@@ -291,7 +295,7 @@ func (s *GDrive) Put(ctx context.Context, token string, filename string, reader
dir := &drive.File{ dir := &drive.File{
Name: token, Name: token,
Parents: []string{s.rootID}, Parents: []string{s.rootID},
MimeType: gdriveDirectoryMimeType, MimeType: gDriveDirectoryMimeType,
Size: int64(contentLength), Size: int64(contentLength),
} }
@@ -321,7 +325,7 @@ func (s *GDrive) Put(ctx context.Context, token string, filename string, reader
// Retrieve a token, saves the token, then returns the generated client. // Retrieve a token, saves the token, then returns the generated client.
func getGDriveClient(ctx context.Context, config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client { func getGDriveClient(ctx context.Context, config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client {
tokenFile := filepath.Join(localConfigPath, gdriveTokenJSONFile) tokenFile := filepath.Join(localConfigPath, gDriveTokenJSONFile)
tok, err := gDriveTokenFromFile(tokenFile) tok, err := gDriveTokenFromFile(tokenFile)
if err != nil { if err != nil {
tok = getGDriveTokenFromWeb(ctx, config, logger) tok = getGDriveTokenFromWeb(ctx, config, logger)

View File

@@ -28,7 +28,7 @@ func (s *LocalStorage) Type() string {
} }
// Head retrieves content length of a file from storage // Head retrieves content length of a file from storage
func (s *LocalStorage) Head(ctx context.Context, token string, filename string) (contentLength uint64, err error) { func (s *LocalStorage) Head(_ context.Context, token string, filename string) (contentLength uint64, err error) {
path := filepath.Join(s.basedir, token, filename) path := filepath.Join(s.basedir, token, filename)
var fi os.FileInfo var fi os.FileInfo
@@ -42,7 +42,7 @@ func (s *LocalStorage) Head(ctx context.Context, token string, filename string)
} }
// Get retrieves a file from storage // Get retrieves a file from storage
func (s *LocalStorage) Get(ctx context.Context, token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) { func (s *LocalStorage) Get(_ context.Context, token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
path := filepath.Join(s.basedir, token, filename) path := filepath.Join(s.basedir, token, filename)
// content type , content length // content type , content length
@@ -61,7 +61,7 @@ func (s *LocalStorage) Get(ctx context.Context, token string, filename string) (
} }
// Delete removes a file from storage // Delete removes a file from storage
func (s *LocalStorage) Delete(ctx context.Context, token string, filename string) (err error) { func (s *LocalStorage) Delete(_ context.Context, token string, filename string) (err error) {
metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename)) metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename))
_ = os.Remove(metadata) _ = os.Remove(metadata)
@@ -71,7 +71,7 @@ func (s *LocalStorage) Delete(ctx context.Context, token string, filename string
} }
// Purge cleans up the storage // Purge cleans up the storage
func (s *LocalStorage) Purge(ctx context.Context, days time.Duration) (err error) { func (s *LocalStorage) Purge(_ context.Context, days time.Duration) (err error) {
err = filepath.Walk(s.basedir, err = filepath.Walk(s.basedir,
func(path string, info os.FileInfo, err error) error { func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
@@ -102,7 +102,7 @@ func (s *LocalStorage) IsNotExist(err error) bool {
} }
// Put saves a file on storage // Put saves a file on storage
func (s *LocalStorage) Put(ctx context.Context, token string, filename string, reader io.Reader, contentType string, contentLength uint64) error { func (s *LocalStorage) Put(_ context.Context, token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
var f io.WriteCloser var f io.WriteCloser
var err error var err error

View File

@@ -68,7 +68,7 @@ func (s *S3Storage) Head(ctx context.Context, token string, filename string) (co
} }
// Purge cleans up the storage // Purge cleans up the storage
func (s *S3Storage) Purge(ctx context.Context, days time.Duration) (err error) { func (s *S3Storage) Purge(context.Context, time.Duration) (err error) {
// NOOP expiration is set at upload time // NOOP expiration is set at upload time
return nil return nil
} }
@@ -136,7 +136,7 @@ func (s *S3Storage) Delete(ctx context.Context, token string, filename string) (
} }
// Put saves a file on storage // Put saves a file on storage
func (s *S3Storage) Put(ctx context.Context, token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) { func (s *S3Storage) Put(ctx context.Context, token string, filename string, reader io.Reader, contentType string, _ uint64) (err error) {
key := fmt.Sprintf("%s/%s", token, filename) key := fmt.Sprintf("%s/%s", token, filename)
s.logger.Printf("Uploading file %s to S3 Bucket", filename) s.logger.Printf("Uploading file %s to S3 Bucket", filename)
@@ -159,10 +159,11 @@ func (s *S3Storage) Put(ctx context.Context, token string, filename string, read
} }
_, err = uploader.UploadWithContext(ctx, &s3manager.UploadInput{ _, err = uploader.UploadWithContext(ctx, &s3manager.UploadInput{
Bucket: aws.String(s.bucket), Bucket: aws.String(s.bucket),
Key: aws.String(key), Key: aws.String(key),
Body: reader, Body: reader,
Expires: expire, Expires: expire,
ContentType: aws.String(contentType),
}) })
return return

View File

@@ -83,7 +83,9 @@ func (s *StorjStorage) Get(ctx context.Context, token string, filename string) (
s.logger.Printf("Getting file %s from Storj Bucket", filename) s.logger.Printf("Getting file %s from Storj Bucket", filename)
download, err := s.project.DownloadObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key, nil) options := uplink.DownloadOptions{}
download, err := s.project.DownloadObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key, &options)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@@ -106,7 +108,7 @@ func (s *StorjStorage) Delete(ctx context.Context, token string, filename string
} }
// Purge cleans up the storage // Purge cleans up the storage
func (s *StorjStorage) Purge(ctx context.Context, days time.Duration) (err error) { func (s *StorjStorage) Purge(context.Context, time.Duration) (err error) {
// NOOP expiration is set at upload time // NOOP expiration is set at upload time
return nil return nil
} }

View File

@@ -30,7 +30,6 @@ import (
"fmt" "fmt"
"math" "math"
"net/http" "net/http"
"net/mail"
"strconv" "strconv"
"strings" "strings"
@@ -189,10 +188,6 @@ func renderFloat(format string, n float64) string {
return signStr + intStr + decimalStr + fracStr return signStr + intStr + decimalStr + fracStr
} }
func renderInteger(format string, n int) string {
return renderFloat(format, float64(n))
}
// Request.RemoteAddress contains port, which we want to remove i.e.: // Request.RemoteAddress contains port, which we want to remove i.e.:
// "[::1]:58292" => "[::1]" // "[::1]:58292" => "[::1]"
func ipAddrFromRemoteAddr(s string) string { func ipAddrFromRemoteAddr(s string) string {
@@ -203,45 +198,16 @@ func ipAddrFromRemoteAddr(s string) string {
return s[:idx] return s[:idx]
} }
func getIPAddress(r *http.Request) string {
hdr := r.Header
hdrRealIP := hdr.Get("X-Real-Ip")
hdrForwardedFor := hdr.Get("X-Forwarded-For")
if hdrRealIP == "" && hdrForwardedFor == "" {
return ipAddrFromRemoteAddr(r.RemoteAddr)
}
if hdrForwardedFor != "" {
// X-Forwarded-For is potentially a list of addresses separated with ","
parts := strings.Split(hdrForwardedFor, ",")
for i, p := range parts {
parts[i] = strings.TrimSpace(p)
}
// TODO: should return first non-local address
return parts[0]
}
return hdrRealIP
}
func encodeRFC2047(s string) string {
// use mail's rfc2047 to encode any string
addr := mail.Address{
Name: s,
Address: "",
}
return strings.Trim(addr.String(), " <>")
}
func acceptsHTML(hdr http.Header) bool { func acceptsHTML(hdr http.Header) bool {
actual := header.ParseAccept(hdr, "Accept") actual := header.ParseAccept(hdr, "Accept")
for _, s := range actual { for _, s := range actual {
if s.Value == "text/html" { if s.Value == "text/html" {
return (true) return true
} }
} }
return (false) return false
} }
func formatSize(size int64) string { func formatSize(size int64) string {

View File

@@ -30,7 +30,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
virustotal "github.com/dutchcoders/go-virustotal" "github.com/dutchcoders/go-virustotal"
) )
func (s *Server) virusTotalHandler(w http.ResponseWriter, r *http.Request) { func (s *Server) virusTotalHandler(w http.ResponseWriter, r *http.Request) {