From aef7e308d08ad34a050287b1d217607c29c5902b Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Thu, 29 Aug 2013 23:39:06 -0400 Subject: [PATCH] Add FTP storage method --- client/storage.go | 7 ++- client/storage_ftp.go | 121 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 client/storage_ftp.go diff --git a/client/storage.go b/client/storage.go index 1ae4f0a..5c62133 100644 --- a/client/storage.go +++ b/client/storage.go @@ -24,8 +24,13 @@ func GetStorage(config *conf.ConfigFile) (Storage, error) { if err != nil { return nil, err } + case "ftp": + storage, err = NewFTPStorage(config) + if err != nil { + return nil, err + } default: - return nil, errors.New("Error: storage method '" + storageMethod + "' not implemented.") + return nil, errors.New("Error: storage method '" + storageMethod + "' not found.") } return storage, nil diff --git a/client/storage_ftp.go b/client/storage_ftp.go new file mode 100644 index 0000000..1d33d05 --- /dev/null +++ b/client/storage_ftp.go @@ -0,0 +1,121 @@ +package main + +import ( + "code.google.com/p/goconf/conf" + "errors" + "github.com/jlaffaye/goftp" + "io" + "os" + "strconv" +) + +const FTP_MAX_CONNECTIONS = 10 //should this be configurable? + +type FTPStorage struct { + connectionsChan chan int + server string + port int + directory string + username string + password string +} + +func NewFTPStorage(config *conf.ConfigFile) (*FTPStorage, error) { + server, err := config.GetString("storage", "server") + if err != nil { + return nil, errors.New("Error: FTPStorage indicated in config file, but 'server' not specified.") + } + port, err := config.GetInt("storage", "port") + if err != nil { + return nil, errors.New("Error: FTPStorage indicated in config file, but 'port' not specified.") + } + directory, err := config.GetString("storage", "directory") + if err != nil { + return nil, errors.New("Error: FTPStorage indicated in config file, but 'directory' not specified.") + } + username, err := config.GetString("storage", "username") + if err != nil { + return nil, errors.New("Error: FTPStorage indicated in config file, but 'username' not specified.") + } + password, err := config.GetString("storage", "password") + if err != nil { + return nil, errors.New("Error: FTPStorage indicated in config file, but 'password' not specified.") + } + + fs := new(FTPStorage) + fs.server = server + fs.port = port + fs.directory = directory + fs.username = username + fs.password = password + + fs.connectionsChan = make(chan int, FTP_MAX_CONNECTIONS) + + return fs, nil +} + +func (fs *FTPStorage) Put(filename string, hash string) (e error) { + //make sure we don't flood the FTP server + fs.connectionsChan <- 0 + defer func() { <-fs.connectionsChan }() + + infile, err := os.Open(filename) + if err != nil { + return err + } + defer infile.Close() + + connection, err := ftp.Connect(fs.server + ":" + strconv.Itoa(fs.port)) + if err != nil { + return err + } + defer connection.Quit() + + err = connection.Login(fs.username, fs.password) + if err != nil { + return err + } + + err = connection.ChangeDir(fs.directory) + if err != nil { + return err + } + + return connection.Stor(hash, infile) +} + +func (fs *FTPStorage) Get(filename string, hash string) error { + fs.connectionsChan <- 0 + defer func() { <-fs.connectionsChan }() + + connection, err := ftp.Connect(fs.server + ":" + strconv.Itoa(fs.port)) + if err != nil { + return err + } + defer connection.Quit() + + err = connection.Login(fs.username, fs.password) + if err != nil { + return err + } + + err = connection.ChangeDir(fs.directory) + if err != nil { + return err + } + + downloadedFile, err := connection.Retr(hash) + if err != nil { + return err + } + defer downloadedFile.Close() + + outfile, err := os.Create(filename) + if err != nil { + return err + } + defer outfile.Close() + + _, err = io.Copy(outfile, downloadedFile) + return err +}