forked from TrueCloudLab/rclone
serve ftp: refactor to bring into line with other serve commands
This commit is contained in:
parent
d75fbe4852
commit
b94eef16c1
4 changed files with 67 additions and 88 deletions
|
@ -16,19 +16,52 @@ import (
|
||||||
|
|
||||||
ftp "github.com/goftp/server"
|
ftp "github.com/goftp/server"
|
||||||
"github.com/rclone/rclone/cmd"
|
"github.com/rclone/rclone/cmd"
|
||||||
"github.com/rclone/rclone/cmd/serve/ftp/ftpflags"
|
|
||||||
"github.com/rclone/rclone/cmd/serve/ftp/ftpopt"
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
"github.com/rclone/rclone/fs/accounting"
|
"github.com/rclone/rclone/fs/accounting"
|
||||||
|
"github.com/rclone/rclone/fs/config/flags"
|
||||||
"github.com/rclone/rclone/fs/log"
|
"github.com/rclone/rclone/fs/log"
|
||||||
|
"github.com/rclone/rclone/fs/rc"
|
||||||
"github.com/rclone/rclone/vfs"
|
"github.com/rclone/rclone/vfs"
|
||||||
"github.com/rclone/rclone/vfs/vfsflags"
|
"github.com/rclone/rclone/vfs/vfsflags"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Options contains options for the http Server
|
||||||
|
type Options struct {
|
||||||
|
//TODO add more options
|
||||||
|
ListenAddr string // Port to listen on
|
||||||
|
PublicIP string // Passive ports range
|
||||||
|
PassivePorts string // Passive ports range
|
||||||
|
BasicUser string // single username for basic auth if not using Htpasswd
|
||||||
|
BasicPass string // password for BasicUser
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultOpt is the default values used for Options
|
||||||
|
var DefaultOpt = Options{
|
||||||
|
ListenAddr: "localhost:2121",
|
||||||
|
PublicIP: "",
|
||||||
|
PassivePorts: "30000-32000",
|
||||||
|
BasicUser: "anonymous",
|
||||||
|
BasicPass: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opt is options set by command line flags
|
||||||
|
var Opt = DefaultOpt
|
||||||
|
|
||||||
|
// AddFlags adds flags for ftp
|
||||||
|
func AddFlags(flagSet *pflag.FlagSet) {
|
||||||
|
rc.AddOption("ftp", &Opt)
|
||||||
|
flags.StringVarP(flagSet, &Opt.ListenAddr, "addr", "", Opt.ListenAddr, "IPaddress:Port or :Port to bind server to.")
|
||||||
|
flags.StringVarP(flagSet, &Opt.PublicIP, "public-ip", "", Opt.PublicIP, "Public IP address to advertise for passive connections.")
|
||||||
|
flags.StringVarP(flagSet, &Opt.PassivePorts, "passive-port", "", Opt.PassivePorts, "Passive port range to use.")
|
||||||
|
flags.StringVarP(flagSet, &Opt.BasicUser, "user", "", Opt.BasicUser, "User name for authentication.")
|
||||||
|
flags.StringVarP(flagSet, &Opt.BasicPass, "pass", "", Opt.BasicPass, "Password for authentication. (empty value allow every password)")
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ftpflags.AddFlags(Command.Flags())
|
|
||||||
vfsflags.AddFlags(Command.Flags())
|
vfsflags.AddFlags(Command.Flags())
|
||||||
|
AddFlags(Command.Flags())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command definition for cobra
|
// Command definition for cobra
|
||||||
|
@ -39,12 +72,28 @@ var Command = &cobra.Command{
|
||||||
rclone serve ftp implements a basic ftp server to serve the
|
rclone serve ftp implements a basic ftp server to serve the
|
||||||
remote over FTP protocol. This can be viewed with a ftp client
|
remote over FTP protocol. This can be viewed with a ftp client
|
||||||
or you can make a remote of type ftp to read and write it.
|
or you can make a remote of type ftp to read and write it.
|
||||||
` + ftpopt.Help + vfs.Help,
|
|
||||||
|
### Server options
|
||||||
|
|
||||||
|
Use --addr to specify which IP address and port the server should
|
||||||
|
listen on, eg --addr 1.2.3.4:8000 or --addr :8080 to listen to all
|
||||||
|
IPs. By default it only listens on localhost. You can use port
|
||||||
|
:0 to let the OS choose an available port.
|
||||||
|
|
||||||
|
If you set --addr to listen on a public or LAN accessible IP address
|
||||||
|
then using Authentication is advised - see the next section for info.
|
||||||
|
|
||||||
|
#### Authentication
|
||||||
|
|
||||||
|
By default this will serve files without needing a login.
|
||||||
|
|
||||||
|
You can set a single username and password with the --user and --pass flags.
|
||||||
|
` + vfs.Help,
|
||||||
Run: func(command *cobra.Command, args []string) {
|
Run: func(command *cobra.Command, args []string) {
|
||||||
cmd.CheckArgs(1, 1, command, args)
|
cmd.CheckArgs(1, 1, command, args)
|
||||||
f := cmd.NewFsSrc(args)
|
f := cmd.NewFsSrc(args)
|
||||||
cmd.Run(false, false, command, func() error {
|
cmd.Run(false, false, command, func() error {
|
||||||
s, err := newServer(f, &ftpflags.Opt)
|
s, err := newServer(f, &Opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -57,10 +106,11 @@ or you can make a remote of type ftp to read and write it.
|
||||||
type server struct {
|
type server struct {
|
||||||
f fs.Fs
|
f fs.Fs
|
||||||
srv *ftp.Server
|
srv *ftp.Server
|
||||||
|
opt Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a new FTP to serve the remote
|
// Make a new FTP to serve the remote
|
||||||
func newServer(f fs.Fs, opt *ftpopt.Options) (*server, error) {
|
func newServer(f fs.Fs, opt *Options) (*server, error) {
|
||||||
host, port, err := net.SplitHostPort(opt.ListenAddr)
|
host, port, err := net.SplitHostPort(opt.ListenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("Failed to parse host:port")
|
return nil, errors.New("Failed to parse host:port")
|
||||||
|
@ -70,6 +120,10 @@ func newServer(f fs.Fs, opt *ftpopt.Options) (*server, error) {
|
||||||
return nil, errors.New("Failed to parse host:port")
|
return nil, errors.New("Failed to parse host:port")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s := &server{
|
||||||
|
f: f,
|
||||||
|
opt: *opt,
|
||||||
|
}
|
||||||
ftpopt := &ftp.ServerOpts{
|
ftpopt := &ftp.ServerOpts{
|
||||||
Name: "Rclone FTP Server",
|
Name: "Rclone FTP Server",
|
||||||
WelcomeMessage: "Welcome on Rclone FTP Server",
|
WelcomeMessage: "Welcome on Rclone FTP Server",
|
||||||
|
@ -80,17 +134,12 @@ func newServer(f fs.Fs, opt *ftpopt.Options) (*server, error) {
|
||||||
Port: portNum,
|
Port: portNum,
|
||||||
PublicIp: opt.PublicIP,
|
PublicIp: opt.PublicIP,
|
||||||
PassivePorts: opt.PassivePorts,
|
PassivePorts: opt.PassivePorts,
|
||||||
Auth: &Auth{
|
Auth: &Auth{s},
|
||||||
BasicUser: opt.BasicUser,
|
Logger: &Logger{},
|
||||||
BasicPass: opt.BasicPass,
|
|
||||||
},
|
|
||||||
Logger: &Logger{},
|
|
||||||
//TODO implement a maximum of https://godoc.org/github.com/goftp/server#ServerOpts
|
//TODO implement a maximum of https://godoc.org/github.com/goftp/server#ServerOpts
|
||||||
}
|
}
|
||||||
return &server{
|
s.srv = ftp.NewServer(ftpopt)
|
||||||
f: f,
|
return s, nil
|
||||||
srv: ftp.NewServer(ftpopt),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// serve runs the ftp server
|
// serve runs the ftp server
|
||||||
|
@ -134,13 +183,12 @@ func (l *Logger) PrintResponse(sessionID string, code int, message string) {
|
||||||
|
|
||||||
//Auth struct to handle ftp auth (temporary simple for POC)
|
//Auth struct to handle ftp auth (temporary simple for POC)
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
BasicUser string
|
s *server
|
||||||
BasicPass string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//CheckPasswd handle auth based on configuration
|
//CheckPasswd handle auth based on configuration
|
||||||
func (a *Auth) CheckPasswd(user, pass string) (bool, error) {
|
func (a *Auth) CheckPasswd(user, pass string) (bool, error) {
|
||||||
return a.BasicUser == user && (a.BasicPass == "" || a.BasicPass == pass), nil
|
return a.s.opt.BasicUser == user && (a.s.opt.BasicPass == "" || a.s.opt.BasicPass == pass), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//DriverFactory factory of ftp driver for each session
|
//DriverFactory factory of ftp driver for each session
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
|
|
||||||
ftp "github.com/goftp/server"
|
ftp "github.com/goftp/server"
|
||||||
_ "github.com/rclone/rclone/backend/local"
|
_ "github.com/rclone/rclone/backend/local"
|
||||||
"github.com/rclone/rclone/cmd/serve/ftp/ftpopt"
|
|
||||||
"github.com/rclone/rclone/fstest"
|
"github.com/rclone/rclone/fstest"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -30,7 +29,7 @@ const (
|
||||||
// TestFTP runs the ftp server then runs the unit tests for the
|
// TestFTP runs the ftp server then runs the unit tests for the
|
||||||
// ftp remote against it.
|
// ftp remote against it.
|
||||||
func TestFTP(t *testing.T) {
|
func TestFTP(t *testing.T) {
|
||||||
opt := ftpopt.DefaultOpt
|
opt := DefaultOpt
|
||||||
opt.ListenAddr = testHOST + ":" + testPORT
|
opt.ListenAddr = testHOST + ":" + testPORT
|
||||||
opt.PassivePorts = testPASSIVEPORTRANGE
|
opt.PassivePorts = testPASSIVEPORTRANGE
|
||||||
opt.BasicUser = "rclone"
|
opt.BasicUser = "rclone"
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
package ftpflags
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/rclone/rclone/cmd/serve/ftp/ftpopt"
|
|
||||||
"github.com/rclone/rclone/fs/config/flags"
|
|
||||||
"github.com/rclone/rclone/fs/rc"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Options set by command line flags
|
|
||||||
var (
|
|
||||||
Opt = ftpopt.DefaultOpt
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddFlagsPrefix adds flags for the ftpopt
|
|
||||||
func AddFlagsPrefix(flagSet *pflag.FlagSet, prefix string, Opt *ftpopt.Options) {
|
|
||||||
rc.AddOption("ftp", &Opt)
|
|
||||||
flags.StringVarP(flagSet, &Opt.ListenAddr, prefix+"addr", "", Opt.ListenAddr, "IPaddress:Port or :Port to bind server to.")
|
|
||||||
flags.StringVarP(flagSet, &Opt.PublicIP, prefix+"public-ip", "", Opt.PublicIP, "Public IP address to advertise for passive connections.")
|
|
||||||
flags.StringVarP(flagSet, &Opt.PassivePorts, prefix+"passive-port", "", Opt.PassivePorts, "Passive port range to use.")
|
|
||||||
flags.StringVarP(flagSet, &Opt.BasicUser, prefix+"user", "", Opt.BasicUser, "User name for authentication.")
|
|
||||||
flags.StringVarP(flagSet, &Opt.BasicPass, prefix+"pass", "", Opt.BasicPass, "Password for authentication. (empty value allow every password)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFlags adds flags for the httplib
|
|
||||||
func AddFlags(flagSet *pflag.FlagSet) {
|
|
||||||
AddFlagsPrefix(flagSet, "", &Opt)
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package ftpopt
|
|
||||||
|
|
||||||
// Help contains text describing the http server to add to the command
|
|
||||||
// help.
|
|
||||||
var Help = `
|
|
||||||
### Server options
|
|
||||||
|
|
||||||
Use --addr to specify which IP address and port the server should
|
|
||||||
listen on, eg --addr 1.2.3.4:8000 or --addr :8080 to listen to all
|
|
||||||
IPs. By default it only listens on localhost. You can use port
|
|
||||||
:0 to let the OS choose an available port.
|
|
||||||
|
|
||||||
If you set --addr to listen on a public or LAN accessible IP address
|
|
||||||
then using Authentication is advised - see the next section for info.
|
|
||||||
|
|
||||||
#### Authentication
|
|
||||||
|
|
||||||
By default this will serve files without needing a login.
|
|
||||||
|
|
||||||
You can set a single username and password with the --user and --pass flags.
|
|
||||||
`
|
|
||||||
|
|
||||||
// Options contains options for the http Server
|
|
||||||
type Options struct {
|
|
||||||
//TODO add more options
|
|
||||||
ListenAddr string // Port to listen on
|
|
||||||
PublicIP string // Passive ports range
|
|
||||||
PassivePorts string // Passive ports range
|
|
||||||
BasicUser string // single username for basic auth if not using Htpasswd
|
|
||||||
BasicPass string // password for BasicUser
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultOpt is the default values used for Options
|
|
||||||
var DefaultOpt = Options{
|
|
||||||
ListenAddr: "localhost:2121",
|
|
||||||
PublicIP: "",
|
|
||||||
PassivePorts: "30000-32000",
|
|
||||||
BasicUser: "anonymous",
|
|
||||||
BasicPass: "",
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue