diff --git a/README.md b/README.md index e1f2a70..b28a338 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ go get github.com/kennygrant/sanitize grunt serve grunt build -sh transfer-server/run.sh +go run transfersh-server/*.go -provider=local --port 8080 --temp=/tmp/ --basedir=/tmp/ ``` ## Build diff --git a/transfersh-server/handlers.go b/transfersh-server/handlers.go index 356fffb..346711e 100644 --- a/transfersh-server/handlers.go +++ b/transfersh-server/handlers.go @@ -35,19 +35,18 @@ import ( "errors" "fmt" "github.com/dutchcoders/go-clamd" - "github.com/goamz/goamz/s3" "github.com/golang/gddo/httputil/header" "github.com/gorilla/mux" "github.com/kennygrant/sanitize" "io" "io/ioutil" + "strconv" "log" "math/rand" "mime" "net/http" "os" "path/filepath" - "strconv" "strings" "time" html_template "html/template" @@ -112,13 +111,6 @@ func postHandler(w http.ResponseWriter, r *http.Request) { return } - bucket, err := getBucket() - if err != nil { - log.Println(err) - http.Error(w, "Error occured copying to output stream", 500) - return - } - token := Encode(10000000 + int64(rand.Intn(1000000000))) w.Header().Set("Content-Type", "text/plain") @@ -175,18 +167,16 @@ func postHandler(w http.ResponseWriter, r *http.Request) { contentLength := n - key := fmt.Sprintf("%s/%s", token, filename) + log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType) - log.Printf("Uploading %s %d %s", key, contentLength, contentType) - - if err = bucket.PutReader(key, reader, contentLength, contentType, s3.PublicRead, s3.Options{}); err != nil { + if err = storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil { log.Print(err) http.Error(w, err.Error(), 500) return } - fmt.Fprintf(w, "https://transfer.sh/%s\n", key) + fmt.Fprintf(w, "https://transfer.sh/%s/%s\n", token, filename) } } } @@ -285,22 +275,13 @@ func putHandler(w http.ResponseWriter, r *http.Request) { contentType = mime.TypeByExtension(filepath.Ext(vars["filename"])) } - key := fmt.Sprintf("%s/%s", Encode(10000000+int64(rand.Intn(1000000000))), filename) + token := Encode(10000000+int64(rand.Intn(1000000000))) - log.Printf("Uploading %s %d %s", key, contentLength, contentType) + log.Printf("Uploading %s %d %s", token, filename, contentLength, contentType) - var b *s3.Bucket var err error - b, err = getBucket() - if err != nil { - http.Error(w, errors.New("Could not open bucket").Error(), 500) - return - } - - err = b.PutReader(key, reader, contentLength, contentType, s3.PublicRead, s3.Options{}) - - if err != nil { + if err = storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil { http.Error(w, errors.New("Could not save file").Error(), 500) return } @@ -309,7 +290,7 @@ func putHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") - fmt.Fprintf(w, "https://transfer.sh/%s\n", key) + fmt.Fprintf(w, "https://transfer.sh/%s/%s\n", token, filename) } func zipHandler(w http.ResponseWriter, r *http.Request) { @@ -317,22 +298,19 @@ func zipHandler(w http.ResponseWriter, r *http.Request) { files := vars["files"] - b, err := getBucket() - if err != nil { - http.Error(w, err.Error(), 500) - return - } - - filename := fmt.Sprintf("transfersh-%d.zip", uint16(time.Now().UnixNano())) + zipfilename := fmt.Sprintf("transfersh-%d.zip", uint16(time.Now().UnixNano())) w.Header().Set("Content-Type", "application/zip") - w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", zipfilename)) w.Header().Set("Connection", "close") zw := zip.NewWriter(w) for _, key := range strings.Split(files, ",") { - rc, err := b.GetResponse(key) + token := sanitize.Path(strings.Split(key, "/")[0]) + filename := sanitize.Path(strings.Split(key, "/")[1]) + + reader, _, _, err := storage.Get(token, filename) if err != nil { if err.Error() == "The specified key does not exist." { http.Error(w, "File not found", 404) @@ -344,7 +322,7 @@ func zipHandler(w http.ResponseWriter, r *http.Request) { } } - defer rc.Body.Close() + defer reader.Close() header := &zip.FileHeader{ Name: strings.Split(key, "/")[1], @@ -353,8 +331,6 @@ func zipHandler(w http.ResponseWriter, r *http.Request) { ModifiedDate: uint16(time.Now().UnixNano()), } - fi := rc.Body - fw, err := zw.CreateHeader(header) if err != nil { @@ -363,7 +339,7 @@ func zipHandler(w http.ResponseWriter, r *http.Request) { return } - if _, err = io.Copy(fw, fi); err != nil { + if _, err = io.Copy(fw, reader); err != nil { log.Printf("%s", err.Error()) http.Error(w, "Internal server error.", 500) return @@ -382,16 +358,10 @@ func tarGzHandler(w http.ResponseWriter, r *http.Request) { files := vars["files"] - b, err := getBucket() - if err != nil { - http.Error(w, err.Error(), 500) - return - } - - filename := fmt.Sprintf("transfersh-%d.tar.gz", uint16(time.Now().UnixNano())) + tarfilename := fmt.Sprintf("transfersh-%d.tar.gz", uint16(time.Now().UnixNano())) w.Header().Set("Content-Type", "application/x-gzip") - w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename)) w.Header().Set("Connection", "close") os := gzip.NewWriter(w) @@ -401,7 +371,10 @@ func tarGzHandler(w http.ResponseWriter, r *http.Request) { defer zw.Close() for _, key := range strings.Split(files, ",") { - rc, err := b.GetResponse(key) + token := strings.Split(key, "/")[0] + filename := strings.Split(key, "/")[1] + + reader, _, contentLength, err := storage.Get(token, filename) if err != nil { if err.Error() == "The specified key does not exist." { http.Error(w, "File not found", 404) @@ -413,9 +386,7 @@ func tarGzHandler(w http.ResponseWriter, r *http.Request) { } } - defer rc.Body.Close() - - contentLength, err := strconv.Atoi(rc.Header.Get("Content-Length")) + defer reader.Close() header := &tar.Header{ Name: strings.Split(key, "/")[1], @@ -429,9 +400,7 @@ func tarGzHandler(w http.ResponseWriter, r *http.Request) { return } - fi := rc.Body - - if _, err = io.Copy(zw, fi); err != nil { + if _, err = io.Copy(zw, reader); err != nil { log.Printf("%s", err.Error()) http.Error(w, "Internal server error.", 500) return @@ -444,23 +413,20 @@ func tarHandler(w http.ResponseWriter, r *http.Request) { files := vars["files"] - b, err := getBucket() - if err != nil { - http.Error(w, err.Error(), 500) - return - } - - filename := fmt.Sprintf("transfersh-%d.tar", uint16(time.Now().UnixNano())) + tarfilename := fmt.Sprintf("transfersh-%d.tar", uint16(time.Now().UnixNano())) w.Header().Set("Content-Type", "application/x-tar") - w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", tarfilename)) w.Header().Set("Connection", "close") zw := tar.NewWriter(w) defer zw.Close() for _, key := range strings.Split(files, ",") { - rc, err := b.GetResponse(key) + token := strings.Split(key, "/")[0] + filename := strings.Split(key, "/")[1] + + reader, _, contentLength, err := storage.Get(token, filename) if err != nil { if err.Error() == "The specified key does not exist." { http.Error(w, "File not found", 404) @@ -472,9 +438,7 @@ func tarHandler(w http.ResponseWriter, r *http.Request) { } } - defer rc.Body.Close() - - contentLength, err := strconv.Atoi(rc.Header.Get("Content-Length")) + defer reader.Close() header := &tar.Header{ Name: strings.Split(key, "/")[1], @@ -488,9 +452,7 @@ func tarHandler(w http.ResponseWriter, r *http.Request) { return } - fi := rc.Body - - if _, err = io.Copy(zw, fi); err != nil { + if _, err = io.Copy(zw, reader); err != nil { log.Printf("%s", err.Error()) http.Error(w, "Internal server error.", 500) return @@ -504,15 +466,7 @@ func getHandler(w http.ResponseWriter, r *http.Request) { token := vars["token"] filename := vars["filename"] - key := fmt.Sprintf("%s/%s", token, filename) - - b, err := getBucket() - if err != nil { - http.Error(w, err.Error(), 500) - return - } - - rc, err := b.GetResponse(key) + reader, contentType, contentLength, err := storage.Get(token, filename) if err != nil { if err.Error() == "The specified key does not exist." { http.Error(w, "File not found", 404) @@ -524,15 +478,13 @@ func getHandler(w http.ResponseWriter, r *http.Request) { } } - defer rc.Body.Close() + defer reader.Close() - contentType := rc.Header.Get("Content-Type") w.Header().Set("Content-Type", contentType) + w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10)) mediaType, _, _ := mime.ParseMediaType(contentType) - fmt.Println(mediaType) - switch { case mediaType == "text/html": w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) @@ -546,7 +498,7 @@ func getHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Connection", "close") - if _, err = io.Copy(w, rc.Body); err != nil { + if _, err = io.Copy(w, reader); err != nil { http.Error(w, "Error occured copying to output stream", 500) return } diff --git a/transfersh-server/main.go b/transfersh-server/main.go index 0ea2803..97af82f 100644 --- a/transfersh-server/main.go +++ b/transfersh-server/main.go @@ -51,6 +51,8 @@ var config struct { Temp string } +var storage Storage + func init() { config.AWS_ACCESS_KEY = os.Getenv("AWS_ACCESS_KEY") config.AWS_SECRET_KEY = os.Getenv("AWS_SECRET_KEY") @@ -111,7 +113,9 @@ func main() { port := flag.String("port", "8080", "port number, default: 8080") temp := flag.String("temp", "", "") + basedir := flag.String("basedir", "", "") logpath := flag.String("log", "", "") + provider := flag.String("provider", "s3", "") flag.Parse() @@ -127,6 +131,24 @@ func main() { } config.Temp = *temp + + var err error + + switch *provider { + case "s3": + storage, err = NewS3Storage() + case "local": + if *basedir == "" { + log.Panic("basedir not set") + } + + storage, err = NewLocalStorage(*basedir) + } + + if err != nil { + log.Panic("Error while creating storage.") + } + log.Printf("Transfer.sh server started. :%v using temp folder: %s", *port, config.Temp) log.Printf("---------------------------") diff --git a/transfersh-server/static/404.txt b/transfersh-server/static/404.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/transfersh-server/static/404.txt @@ -0,0 +1 @@ + diff --git a/transfersh-server/storage.go b/transfersh-server/storage.go new file mode 100644 index 0000000..5bb2d15 --- /dev/null +++ b/transfersh-server/storage.go @@ -0,0 +1,100 @@ +package main + +import ( + "io" + "github.com/goamz/goamz/s3" + "strconv" + "fmt" + "os" + "path/filepath" +) + +type Storage interface { + Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) + Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error +} + +type LocalStorage struct { + Storage + basedir string +} + +func NewLocalStorage(basedir string) (*LocalStorage, error) { + return &LocalStorage {basedir: basedir}, nil +} + + +func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) { + path := filepath.Join(s.basedir, token, filename) + + // content type , content length + if reader, err = os.Open(path); err != nil { + return + } + + var fi os.FileInfo + if fi, err = os.Lstat(path); err != nil { + } + + contentLength = uint64(fi.Size()) + + contentType = "" + + return +} + +func (s *LocalStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error { + var f io.WriteCloser + var err error + + path := filepath.Join(s.basedir, token) + + if err = os.Mkdir(path, 0700); err != nil && !os.IsExist(err) { + return err + } + + if f, err = os.OpenFile(filepath.Join(path, filename), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600); err != nil { + fmt.Printf("%s", err) + return err + } + + defer f.Close() + + if _, err = io.Copy(f, reader); err != nil { + return err + } + + return nil +} + +type S3Storage struct { + Storage + bucket *s3.Bucket +} + +func NewS3Storage() (*S3Storage, error) { + bucket, err := getBucket() + if err != nil { + return nil, err + } + + return &S3Storage {bucket: bucket}, nil +} + +func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) { + key := fmt.Sprintf("%s/%s", token, filename) + + // content type , content length + response, err := s.bucket.GetResponse(key) + contentType = "" + contentLength, err = strconv.ParseUint(response.Header.Get("Content-Length"), 10, 0) + + reader = response.Body + return +} + +func (s *S3Storage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error { + key := fmt.Sprintf("%s/%s", token, filename) + err := s.bucket.PutReader(key, reader, int64(contentLength), contentType, s3.Private, s3.Options{}) + return err +}