@@ -1,6 +1,8 @@ | |||
# transfer.sh | |||
Easy and fast file sharing from the command-line. This code contains the server with everything you need to create your own instance. Transfer.sh currently runs on top of Amazon S3. Other storage types will be added shortly. | |||
Easy and fast file sharing from the command-line. This code contains the server with everything you need to create your own instance. | |||
Transfer.sh support currently the s3 (Amazon S3) provider and local file system (local). | |||
[![Build Status](https://travis-ci.org/dutchcoders/transfer.sh.svg?branch=master)](https://travis-ci.org/dutchcoders/transfer.sh) | |||
@@ -38,19 +38,19 @@ import ( | |||
"github.com/golang/gddo/httputil/header" | |||
"github.com/gorilla/mux" | |||
"github.com/kennygrant/sanitize" | |||
html_template "html/template" | |||
"io" | |||
"io/ioutil" | |||
"strconv" | |||
"log" | |||
"math/rand" | |||
"mime" | |||
"net/http" | |||
"os" | |||
"path/filepath" | |||
"strconv" | |||
"strings" | |||
"time" | |||
html_template "html/template" | |||
text_template "text/template" | |||
"time" | |||
) | |||
func healthHandler(w http.ResponseWriter, r *http.Request) { | |||
@@ -101,7 +101,7 @@ func viewHandler(w http.ResponseWriter, r *http.Request) { | |||
} | |||
func notFoundHandler(w http.ResponseWriter, r *http.Request) { | |||
http.Error(w, http.StatusText(404), 404) | |||
http.Error(w, http.StatusText(404), 404) | |||
} | |||
func postHandler(w http.ResponseWriter, r *http.Request) { | |||
@@ -169,7 +169,7 @@ func postHandler(w http.ResponseWriter, r *http.Request) { | |||
log.Printf("Uploading %s %s %d %s", token, filename, contentLength, contentType) | |||
if err = storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil { | |||
if err = storage.Put(token, filename, reader, contentType, uint64(contentLength)); err != nil { | |||
log.Print(err) | |||
http.Error(w, err.Error(), 500) | |||
return | |||
@@ -275,7 +275,7 @@ func putHandler(w http.ResponseWriter, r *http.Request) { | |||
contentType = mime.TypeByExtension(filepath.Ext(vars["filename"])) | |||
} | |||
token := Encode(10000000+int64(rand.Intn(1000000000))) | |||
token := Encode(10000000 + int64(rand.Intn(1000000000))) | |||
log.Printf("Uploading %s %d %s", token, filename, contentLength, contentType) | |||
@@ -307,8 +307,8 @@ func zipHandler(w http.ResponseWriter, r *http.Request) { | |||
zw := zip.NewWriter(w) | |||
for _, key := range strings.Split(files, ",") { | |||
token := sanitize.Path(strings.Split(key, "/")[0]) | |||
filename := sanitize.Path(strings.Split(key, "/")[1]) | |||
token := sanitize.Path(strings.Split(key, "/")[0]) | |||
filename := sanitize.Path(strings.Split(key, "/")[1]) | |||
reader, _, _, err := storage.Get(token, filename) | |||
if err != nil { | |||
@@ -322,7 +322,7 @@ func zipHandler(w http.ResponseWriter, r *http.Request) { | |||
} | |||
} | |||
defer reader.Close() | |||
defer reader.Close() | |||
header := &zip.FileHeader{ | |||
Name: strings.Split(key, "/")[1], | |||
@@ -371,8 +371,8 @@ func tarGzHandler(w http.ResponseWriter, r *http.Request) { | |||
defer zw.Close() | |||
for _, key := range strings.Split(files, ",") { | |||
token := strings.Split(key, "/")[0] | |||
filename := strings.Split(key, "/")[1] | |||
token := strings.Split(key, "/")[0] | |||
filename := strings.Split(key, "/")[1] | |||
reader, _, contentLength, err := storage.Get(token, filename) | |||
if err != nil { | |||
@@ -386,7 +386,7 @@ func tarGzHandler(w http.ResponseWriter, r *http.Request) { | |||
} | |||
} | |||
defer reader.Close() | |||
defer reader.Close() | |||
header := &tar.Header{ | |||
Name: strings.Split(key, "/")[1], | |||
@@ -423,8 +423,8 @@ func tarHandler(w http.ResponseWriter, r *http.Request) { | |||
defer zw.Close() | |||
for _, key := range strings.Split(files, ",") { | |||
token := strings.Split(key, "/")[0] | |||
filename := strings.Split(key, "/")[1] | |||
token := strings.Split(key, "/")[0] | |||
filename := strings.Split(key, "/")[1] | |||
reader, _, contentLength, err := storage.Get(token, filename) | |||
if err != nil { | |||
@@ -438,7 +438,7 @@ func tarHandler(w http.ResponseWriter, r *http.Request) { | |||
} | |||
} | |||
defer reader.Close() | |||
defer reader.Close() | |||
header := &tar.Header{ | |||
Name: strings.Split(key, "/")[1], | |||
@@ -466,7 +466,7 @@ func getHandler(w http.ResponseWriter, r *http.Request) { | |||
token := vars["token"] | |||
filename := vars["filename"] | |||
reader, contentType, contentLength, err := storage.Get(token, filename) | |||
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) | |||
@@ -478,10 +478,10 @@ func getHandler(w http.ResponseWriter, r *http.Request) { | |||
} | |||
} | |||
defer reader.Close() | |||
defer reader.Close() | |||
w.Header().Set("Content-Type", contentType) | |||
w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10)) | |||
w.Header().Set("Content-Length", strconv.FormatUint(contentLength, 10)) | |||
mediaType, _, _ := mime.ParseMediaType(contentType) | |||
@@ -131,23 +131,23 @@ 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.") | |||
} | |||
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("---------------------------") | |||
@@ -1,100 +1,99 @@ | |||
package main | |||
import ( | |||
"io" | |||
"github.com/goamz/goamz/s3" | |||
"strconv" | |||
"fmt" | |||
"os" | |||
"path/filepath" | |||
"fmt" | |||
"github.com/goamz/goamz/s3" | |||
"io" | |||
"os" | |||
"path/filepath" | |||
"strconv" | |||
) | |||
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 | |||
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 | |||
Storage | |||
basedir string | |||
} | |||
func NewLocalStorage(basedir string) (*LocalStorage, error) { | |||
return &LocalStorage {basedir: basedir}, nil | |||
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) | |||
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 | |||
} | |||
// 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 { | |||
} | |||
var fi os.FileInfo | |||
if fi, err = os.Lstat(path); err != nil { | |||
} | |||
contentLength = uint64(fi.Size()) | |||
contentLength = uint64(fi.Size()) | |||
contentType = "" | |||
contentType = "" | |||
return | |||
return | |||
} | |||
func (s *LocalStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error { | |||
var f io.WriteCloser | |||
var err error | |||
var f io.WriteCloser | |||
var err error | |||
path := filepath.Join(s.basedir, token) | |||
path := filepath.Join(s.basedir, token) | |||
if err = os.Mkdir(path, 0700); err != nil && !os.IsExist(err) { | |||
return err | |||
} | |||
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 | |||
} | |||
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() | |||
defer f.Close() | |||
if _, err = io.Copy(f, reader); err != nil { | |||
return err | |||
} | |||
if _, err = io.Copy(f, reader); err != nil { | |||
return err | |||
} | |||
return nil | |||
return nil | |||
} | |||
type S3Storage struct { | |||
Storage | |||
bucket *s3.Bucket | |||
Storage | |||
bucket *s3.Bucket | |||
} | |||
func NewS3Storage() (*S3Storage, error) { | |||
bucket, err := getBucket() | |||
if err != nil { | |||
return nil, err | |||
} | |||
bucket, err := getBucket() | |||
if err != nil { | |||
return nil, err | |||
} | |||
return &S3Storage {bucket: bucket}, nil | |||
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) | |||
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) | |||
// 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 | |||
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 | |||
key := fmt.Sprintf("%s/%s", token, filename) | |||
err := s.bucket.PutReader(key, reader, int64(contentLength), contentType, s3.Private, s3.Options{}) | |||
return err | |||
} |