@@ -220,7 +220,7 @@ type Cmd struct { | |||||
} | } | ||||
func VersionAction(c *cli.Context) { | func VersionAction(c *cli.Context) { | ||||
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line"))) | |||||
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line, version: %s", c.App.Version))) | |||||
} | } | ||||
func New() *Cmd { | func New() *Cmd { | ||||
@@ -246,7 +246,7 @@ func New() *Cmd { | |||||
} | } | ||||
app.Action = func(c *cli.Context) { | app.Action = func(c *cli.Context) { | ||||
options := []server.OptionFn{} | |||||
var options []server.OptionFn | |||||
if v := c.String("listener"); v != "" { | if v := c.String("listener"); v != "" { | ||||
options = append(options, server.Listener(v)) | options = append(options, server.Listener(v)) | ||||
} | } | ||||
@@ -354,10 +354,11 @@ func New() *Cmd { | |||||
panic("secret-key not set.") | panic("secret-key not set.") | ||||
} else if bucket := c.String("bucket"); bucket == "" { | } else if bucket := c.String("bucket"); bucket == "" { | ||||
panic("bucket not set.") | panic("bucket not set.") | ||||
} else if storage, err := storage.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-region"), c.String("s3-endpoint"), logger, c.Bool("s3-no-multipart"), c.Bool("s3-path-style")); err != nil { | |||||
} else if awsStorage, err := storage.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-region"), | |||||
c.String("s3-endpoint"), logger, c.Bool("s3-no-multipart"), c.Bool("s3-path-style")); err != nil { | |||||
panic(err) | panic(err) | ||||
} else { | } else { | ||||
options = append(options, server.UseStorage(storage)) | |||||
options = append(options, server.UseStorage(awsStorage)) | |||||
} | } | ||||
case "gdrive": | case "gdrive": | ||||
chunkSize := c.Int("gdrive-chunk-size") | chunkSize := c.Int("gdrive-chunk-size") | ||||
@@ -368,18 +369,18 @@ func New() *Cmd { | |||||
panic("local-config-path not set.") | panic("local-config-path not set.") | ||||
} else if basedir := c.String("basedir"); basedir == "" { | } else if basedir := c.String("basedir"); basedir == "" { | ||||
panic("basedir not set.") | panic("basedir not set.") | ||||
} else if storage, err := storage.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil { | |||||
} else if gStorage, err := storage.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil { | |||||
panic(err) | panic(err) | ||||
} else { | } else { | ||||
options = append(options, server.UseStorage(storage)) | |||||
options = append(options, server.UseStorage(gStorage)) | |||||
} | } | ||||
case "local": | case "local": | ||||
if v := c.String("basedir"); v == "" { | if v := c.String("basedir"); v == "" { | ||||
panic("basedir not set.") | panic("basedir not set.") | ||||
} else if storage, err := storage.NewLocalStorage(v, logger); err != nil { | |||||
} else if localStorage, err := storage.NewLocalStorage(v, logger); err != nil { | |||||
panic(err) | panic(err) | ||||
} else { | } else { | ||||
options = append(options, server.UseStorage(storage)) | |||||
options = append(options, server.UseStorage(localStorage)) | |||||
} | } | ||||
default: | default: | ||||
panic("Provider not set or invalid.") | panic("Provider not set or invalid.") | ||||
@@ -477,18 +477,18 @@ func resolveKey(key, proxyPath string) string { | |||||
} | } | ||||
func resolveWebAddress(r *http.Request, proxyPath string) string { | func resolveWebAddress(r *http.Request, proxyPath string) string { | ||||
url := getURL(r) | |||||
rUrl := getURL(r) | |||||
var webAddress string | var webAddress string | ||||
if len(proxyPath) == 0 { | if len(proxyPath) == 0 { | ||||
webAddress = fmt.Sprintf("%s://%s/", | webAddress = fmt.Sprintf("%s://%s/", | ||||
url.ResolveReference(url).Scheme, | |||||
url.ResolveReference(url).Host) | |||||
rUrl.ResolveReference(rUrl).Scheme, | |||||
rUrl.ResolveReference(rUrl).Host) | |||||
} else { | } else { | ||||
webAddress = fmt.Sprintf("%s://%s/%s", | webAddress = fmt.Sprintf("%s://%s/%s", | ||||
url.ResolveReference(url).Scheme, | |||||
url.ResolveReference(url).Host, | |||||
rUrl.ResolveReference(rUrl).Scheme, | |||||
rUrl.ResolveReference(rUrl).Host, | |||||
proxyPath) | proxyPath) | ||||
} | } | ||||
@@ -522,7 +522,7 @@ func getURL(r *http.Request) *url.URL { | |||||
return u | return u | ||||
} | } | ||||
func (s *Server) Lock(token, filename string) error { | |||||
func (s *Server) Lock(token, filename string) { | |||||
key := path.Join(token, filename) | key := path.Join(token, filename) | ||||
if _, ok := s.locks[key]; !ok { | if _, ok := s.locks[key]; !ok { | ||||
@@ -530,15 +530,11 @@ func (s *Server) Lock(token, filename string) error { | |||||
} | } | ||||
s.locks[key].Lock() | s.locks[key].Lock() | ||||
return nil | |||||
} | } | ||||
func (s *Server) Unlock(token, filename string) error { | |||||
func (s *Server) Unlock(token, filename string) { | |||||
key := path.Join(token, filename) | key := path.Join(token, filename) | ||||
s.locks[key].Unlock() | s.locks[key].Unlock() | ||||
return nil | |||||
} | } | ||||
func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (metadata storage.Metadata, err error) { | func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (metadata storage.Metadata, err error) { | ||||
@@ -553,11 +549,10 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (m | |||||
} | } | ||||
if metadata.MaxDownloads != -1 && metadata.Downloads >= metadata.MaxDownloads { | if metadata.MaxDownloads != -1 && metadata.Downloads >= metadata.MaxDownloads { | ||||
return metadata, errors.New("MaxDownloads expired.") | |||||
return metadata, errors.New("max downloads exceeded") | |||||
} else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) { | } else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) { | ||||
return metadata, errors.New("MaxDate expired.") | |||||
return metadata, errors.New("file access expired") | |||||
} else { | } else { | ||||
// todo(nl5887): mutex? | |||||
// update number of downloads | // update number of downloads | ||||
if increaseDownload { | if increaseDownload { | ||||
@@ -565,7 +560,7 @@ func (s *Server) CheckMetadata(token, filename string, increaseDownload bool) (m | |||||
} | } | ||||
if err := s.storage.Meta(token, filename, metadata); err != nil { | if err := s.storage.Meta(token, filename, metadata); err != nil { | ||||
return metadata, errors.New("Could not save metadata") | |||||
return metadata, errors.New("could not save metadata") | |||||
} | } | ||||
} | } | ||||
@@ -585,7 +580,7 @@ func (s *Server) CheckDeletionToken(deletionToken, token, filename string) error | |||||
} | } | ||||
if metadata.DeletionToken != deletionToken { | if metadata.DeletionToken != deletionToken { | ||||
return errors.New("Deletion token doesn't match.") | |||||
return errors.New("deletion token does not match") | |||||
} | } | ||||
return nil | return nil | ||||
@@ -655,10 +650,9 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) { | |||||
defer reader.Close() | defer reader.Close() | ||||
header := &zip.FileHeader{ | 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(), | |||||
} | } | ||||
fw, err := zw.CreateHeader(header) | fw, err := zw.CreateHeader(header) | ||||
@@ -26,14 +26,17 @@ package server | |||||
import ( | import ( | ||||
"context" | "context" | ||||
"crypto/tls" | |||||
"errors" | "errors" | ||||
"log" | "log" | ||||
"math/rand" | "math/rand" | ||||
"mime" | "mime" | ||||
"net/http" | "net/http" | ||||
_ "net/http/pprof" | |||||
"net/url" | "net/url" | ||||
"os" | "os" | ||||
"os/signal" | "os/signal" | ||||
"path/filepath" | |||||
"strings" | "strings" | ||||
"sync" | "sync" | ||||
"syscall" | "syscall" | ||||
@@ -42,22 +45,13 @@ import ( | |||||
"github.com/PuerkitoBio/ghost/handlers" | "github.com/PuerkitoBio/ghost/handlers" | ||||
"github.com/VojtechVitek/ratelimit" | "github.com/VojtechVitek/ratelimit" | ||||
"github.com/VojtechVitek/ratelimit/memory" | "github.com/VojtechVitek/ratelimit/memory" | ||||
"github.com/dutchcoders/transfer.sh/server/storage" | |||||
"github.com/gorilla/mux" | |||||
_ "net/http/pprof" | |||||
"crypto/tls" | |||||
web "github.com/dutchcoders/transfer.sh-web" | web "github.com/dutchcoders/transfer.sh-web" | ||||
"github.com/dutchcoders/transfer.sh/server/storage" | |||||
assetfs "github.com/elazarl/go-bindata-assetfs" | assetfs "github.com/elazarl/go-bindata-assetfs" | ||||
autocert "golang.org/x/crypto/acme/autocert" | |||||
"path/filepath" | |||||
"github.com/gorilla/mux" | |||||
"golang.org/x/crypto/acme/autocert" | |||||
) | ) | ||||
const SERVER_INFO = "transfer.sh" | |||||
// parse request with maximum memory of _24Kilobits | // parse request with maximum memory of _24Kilobits | ||||
const _24K = (1 << 3) * 24 | const _24K = (1 << 3) * 24 | ||||
@@ -317,7 +311,7 @@ func (s *Server) Run() { | |||||
go func() { | go func() { | ||||
s.logger.Println("Profiled listening at: :6060") | s.logger.Println("Profiled listening at: :6060") | ||||
http.ListenAndServe(":6060", nil) | |||||
_ = http.ListenAndServe(":6060", nil) | |||||
}() | }() | ||||
} | } | ||||
@@ -348,8 +342,8 @@ func (s *Server) Run() { | |||||
s.logger.Panicf("Unable to parse: path=%s, err=%s", path, err) | 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)) | |||||
_, _ = htmlTemplates.New(stripPrefix(path)).Parse(string(bytes)) | |||||
_, _ = textTemplates.New(stripPrefix(path)).Parse(string(bytes)) | |||||
} | } | ||||
} | } | ||||
@@ -385,7 +379,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 { | ||||
@@ -417,7 +411,7 @@ func (s *Server) Run() { | |||||
r.NotFoundHandler = http.HandlerFunc(s.notFoundHandler) | 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()) | s.logger.Printf("Transfer.sh server started.\nusing temp folder: %s\nusing storage provider: %s", s.tempPath, s.storage.Type()) | ||||
@@ -443,7 +437,7 @@ func (s *Server) Run() { | |||||
s.logger.Printf("listening on port: %v\n", s.ListenerString) | s.logger.Printf("listening on port: %v\n", s.ListenerString) | ||||
go func() { | go func() { | ||||
srvr.ListenAndServe() | |||||
_ = srvr.ListenAndServe() | |||||
}() | }() | ||||
} | } | ||||
@@ -60,104 +60,6 @@ func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir | |||||
return storage, nil | return storage, nil | ||||
} | } | ||||
func (s *GDrive) setupRoot() error { | |||||
rootFileConfig := filepath.Join(s.localConfigPath, GDriveRootConfigFile) | |||||
rootId, err := ioutil.ReadFile(rootFileConfig) | |||||
if err != nil && !os.IsNotExist(err) { | |||||
return err | |||||
} | |||||
if string(rootId) != "" { | |||||
s.rootId = string(rootId) | |||||
return nil | |||||
} | |||||
dir := &drive.File{ | |||||
Name: s.basedir, | |||||
MimeType: GDriveDirectoryMimeType, | |||||
} | |||||
di, err := s.service.Files.Create(dir).Fields("id").Do() | |||||
if err != nil { | |||||
return err | |||||
} | |||||
s.rootId = di.Id | |||||
err = ioutil.WriteFile(rootFileConfig, []byte(s.rootId), os.FileMode(0600)) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return nil | |||||
} | |||||
func (s *GDrive) hasChecksum(f *drive.File) bool { | |||||
return f.Md5Checksum != "" | |||||
} | |||||
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) { | |||||
filename = strings.Replace(filename, `'`, `\'`, -1) | |||||
filename = strings.Replace(filename, `"`, `\"`, -1) | |||||
fileId, tokenId, nextPageToken := "", "", "" | |||||
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 | |||||
} | |||||
for 0 < len(l.Files) { | |||||
for _, fi := range l.Files { | |||||
tokenId = fi.Id | |||||
break | |||||
} | |||||
if l.NextPageToken == "" { | |||||
break | |||||
} | |||||
l, err = s.list(l.NextPageToken, q) | |||||
} | |||||
if filename == "" { | |||||
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) | |||||
l, err = s.list(nextPageToken, q) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
for 0 < len(l.Files) { | |||||
for _, fi := range l.Files { | |||||
fileId = fi.Id | |||||
break | |||||
} | |||||
if l.NextPageToken == "" { | |||||
break | |||||
} | |||||
l, err = s.list(l.NextPageToken, q) | |||||
} | |||||
if fileId == "" { | |||||
return "", fmt.Errorf("Cannot find file %s/%s", token, filename) | |||||
} | |||||
return fileId, nil | |||||
} | |||||
func (s *GDrive) Type() string { | func (s *GDrive) Type() string { | ||||
return "gdrive" | return "gdrive" | ||||
} | } | ||||
@@ -329,6 +231,104 @@ func (s *GDrive) DeleteExpired() error { | |||||
return nil | return nil | ||||
} | } | ||||
func (s *GDrive) setupRoot() error { | |||||
rootFileConfig := filepath.Join(s.localConfigPath, GDriveRootConfigFile) | |||||
rootId, err := ioutil.ReadFile(rootFileConfig) | |||||
if err != nil && !os.IsNotExist(err) { | |||||
return err | |||||
} | |||||
if string(rootId) != "" { | |||||
s.rootId = string(rootId) | |||||
return nil | |||||
} | |||||
dir := &drive.File{ | |||||
Name: s.basedir, | |||||
MimeType: GDriveDirectoryMimeType, | |||||
} | |||||
di, err := s.service.Files.Create(dir).Fields("id").Do() | |||||
if err != nil { | |||||
return err | |||||
} | |||||
s.rootId = di.Id | |||||
err = ioutil.WriteFile(rootFileConfig, []byte(s.rootId), os.FileMode(0600)) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
return nil | |||||
} | |||||
func (s *GDrive) hasChecksum(f *drive.File) bool { | |||||
return f.Md5Checksum != "" | |||||
} | |||||
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) { | |||||
filename = strings.Replace(filename, `'`, `\'`, -1) | |||||
filename = strings.Replace(filename, `"`, `\"`, -1) | |||||
fileId, tokenId, nextPageToken := "", "", "" | |||||
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 | |||||
} | |||||
for 0 < len(l.Files) { | |||||
for _, fi := range l.Files { | |||||
tokenId = fi.Id | |||||
break | |||||
} | |||||
if l.NextPageToken == "" { | |||||
break | |||||
} | |||||
l, err = s.list(l.NextPageToken, q) | |||||
} | |||||
if filename == "" { | |||||
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) | |||||
l, err = s.list(nextPageToken, q) | |||||
if err != nil { | |||||
return "", err | |||||
} | |||||
for 0 < len(l.Files) { | |||||
for _, fi := range l.Files { | |||||
fileId = fi.Id | |||||
break | |||||
} | |||||
if l.NextPageToken == "" { | |||||
break | |||||
} | |||||
l, err = s.list(l.NextPageToken, q) | |||||
} | |||||
if fileId == "" { | |||||
return "", fmt.Errorf("Cannot find file %s/%s", token, filename) | |||||
} | |||||
return fileId, nil | |||||
} | |||||
// Retrieve a token, saves the token, then returns the generated client. | // Retrieve a token, saves the token, then returns the generated client. | ||||
func getGDriveClient(config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client { | func getGDriveClient(config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client { | ||||
tokenFile := filepath.Join(localConfigPath, GDriveTokenJsonFile) | tokenFile := filepath.Join(localConfigPath, GDriveTokenJsonFile) | ||||
@@ -73,6 +73,27 @@ func (s *LocalStorage) Put(token string, filename string, reader io.Reader, meta | |||||
return err | return err | ||||
} | } | ||||
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 | |||||
} | |||||
func (s *LocalStorage) put(token string, filename string, reader io.Reader) error { | func (s *LocalStorage) put(token string, filename string, reader io.Reader) error { | ||||
var f io.WriteCloser | var f io.WriteCloser | ||||
var err error | var err error | ||||
@@ -90,6 +111,7 @@ func (s *LocalStorage) put(token string, filename string, reader io.Reader) erro | |||||
defer f.Close() | defer f.Close() | ||||
_, err = io.Copy(f, reader) | _, err = io.Copy(f, reader) | ||||
return err | return err | ||||
} | } | ||||
@@ -105,24 +127,3 @@ func (s *LocalStorage) putMetadata(token string, filename string, metadata Metad | |||||
} | } | ||||
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 | |||||
} |