@@ -220,7 +220,7 @@ type Cmd struct { | |||
} | |||
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 { | |||
@@ -246,7 +246,7 @@ func New() *Cmd { | |||
} | |||
app.Action = func(c *cli.Context) { | |||
options := []server.OptionFn{} | |||
var options []server.OptionFn | |||
if v := c.String("listener"); v != "" { | |||
options = append(options, server.Listener(v)) | |||
} | |||
@@ -354,10 +354,11 @@ func New() *Cmd { | |||
panic("secret-key not set.") | |||
} else if bucket := c.String("bucket"); bucket == "" { | |||
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) | |||
} else { | |||
options = append(options, server.UseStorage(storage)) | |||
options = append(options, server.UseStorage(awsStorage)) | |||
} | |||
case "gdrive": | |||
chunkSize := c.Int("gdrive-chunk-size") | |||
@@ -368,18 +369,18 @@ func New() *Cmd { | |||
panic("local-config-path not set.") | |||
} else if basedir := c.String("basedir"); basedir == "" { | |||
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) | |||
} else { | |||
options = append(options, server.UseStorage(storage)) | |||
options = append(options, server.UseStorage(gStorage)) | |||
} | |||
case "local": | |||
if v := c.String("basedir"); v == "" { | |||
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) | |||
} else { | |||
options = append(options, server.UseStorage(storage)) | |||
options = append(options, server.UseStorage(localStorage)) | |||
} | |||
default: | |||
panic("Provider not set or invalid.") | |||
@@ -477,18 +477,18 @@ func resolveKey(key, proxyPath string) string { | |||
} | |||
func resolveWebAddress(r *http.Request, proxyPath string) string { | |||
url := getURL(r) | |||
rUrl := getURL(r) | |||
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) | |||
} | |||
@@ -522,7 +522,7 @@ func getURL(r *http.Request) *url.URL { | |||
return u | |||
} | |||
func (s *Server) Lock(token, filename string) error { | |||
func (s *Server) Lock(token, filename string) { | |||
key := path.Join(token, filename) | |||
if _, ok := s.locks[key]; !ok { | |||
@@ -530,15 +530,11 @@ func (s *Server) Lock(token, filename string) error { | |||
} | |||
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) | |||
s.locks[key].Unlock() | |||
return nil | |||
} | |||
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 { | |||
return metadata, errors.New("MaxDownloads expired.") | |||
return metadata, errors.New("max downloads exceeded") | |||
} else if !metadata.MaxDate.IsZero() && time.Now().After(metadata.MaxDate) { | |||
return metadata, errors.New("MaxDate expired.") | |||
return metadata, errors.New("file access expired") | |||
} else { | |||
// todo(nl5887): mutex? | |||
// update number of downloads | |||
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 { | |||
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 { | |||
return errors.New("Deletion token doesn't match.") | |||
return errors.New("deletion token does not match") | |||
} | |||
return nil | |||
@@ -655,10 +650,9 @@ func (s *Server) zipHandler(w http.ResponseWriter, r *http.Request) { | |||
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(), | |||
} | |||
fw, err := zw.CreateHeader(header) | |||
@@ -26,14 +26,17 @@ package server | |||
import ( | |||
"context" | |||
"crypto/tls" | |||
"errors" | |||
"log" | |||
"math/rand" | |||
"mime" | |||
"net/http" | |||
_ "net/http/pprof" | |||
"net/url" | |||
"os" | |||
"os/signal" | |||
"path/filepath" | |||
"strings" | |||
"sync" | |||
"syscall" | |||
@@ -42,22 +45,13 @@ import ( | |||
"github.com/PuerkitoBio/ghost/handlers" | |||
"github.com/VojtechVitek/ratelimit" | |||
"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" | |||
"github.com/dutchcoders/transfer.sh/server/storage" | |||
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 | |||
const _24K = (1 << 3) * 24 | |||
@@ -317,7 +311,7 @@ func (s *Server) Run() { | |||
go func() { | |||
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) | |||
} | |||
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 | |||
} | |||
match = (r.Referer() == "") | |||
match = r.Referer() == "" | |||
u, err := url.Parse(r.Referer()) | |||
if err != nil { | |||
@@ -417,7 +411,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()) | |||
@@ -443,7 +437,7 @@ func (s *Server) Run() { | |||
s.logger.Printf("listening on port: %v\n", s.ListenerString) | |||
go func() { | |||
srvr.ListenAndServe() | |||
_ = srvr.ListenAndServe() | |||
}() | |||
} | |||
@@ -60,104 +60,6 @@ func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir | |||
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 { | |||
return "gdrive" | |||
} | |||
@@ -329,6 +231,104 @@ func (s *GDrive) DeleteExpired() error { | |||
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. | |||
func getGDriveClient(config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client { | |||
tokenFile := filepath.Join(localConfigPath, GDriveTokenJsonFile) | |||
@@ -73,6 +73,27 @@ func (s *LocalStorage) Put(token string, filename string, reader io.Reader, meta | |||
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 { | |||
var f io.WriteCloser | |||
var err error | |||
@@ -90,6 +111,7 @@ func (s *LocalStorage) put(token string, filename string, reader io.Reader) erro | |||
defer f.Close() | |||
_, err = io.Copy(f, reader) | |||
return err | |||
} | |||
@@ -105,24 +127,3 @@ func (s *LocalStorage) putMetadata(token string, filename string, metadata Metad | |||
} | |||
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 | |||
} |