1
0
mirror of https://github.com/rclone/rclone.git synced 2025-02-04 05:08:23 +02:00

ftp: use lib/encoder

This commit is contained in:
Nick Craig-Wood 2019-07-27 16:58:33 +01:00
parent ced2616da5
commit 32af4cd6f3
4 changed files with 74 additions and 16 deletions

View File

@ -17,11 +17,14 @@ import (
"github.com/rclone/rclone/fs/config/configmap" "github.com/rclone/rclone/fs/config/configmap"
"github.com/rclone/rclone/fs/config/configstruct" "github.com/rclone/rclone/fs/config/configstruct"
"github.com/rclone/rclone/fs/config/obscure" "github.com/rclone/rclone/fs/config/obscure"
"github.com/rclone/rclone/fs/encodings"
"github.com/rclone/rclone/fs/hash" "github.com/rclone/rclone/fs/hash"
"github.com/rclone/rclone/lib/pacer" "github.com/rclone/rclone/lib/pacer"
"github.com/rclone/rclone/lib/readers" "github.com/rclone/rclone/lib/readers"
) )
const enc = encodings.FTP
// Register with Fs // Register with Fs
func init() { func init() {
fs.Register(&fs.RegInfo{ fs.Register(&fs.RegInfo{
@ -295,6 +298,25 @@ func translateErrorDir(err error) error {
return err return err
} }
// entryToStandard converts an incoming ftp.Entry to Standard encoding
func entryToStandard(entry *ftp.Entry) {
// Skip . and .. as we don't want these encoded
if entry.Name == "." || entry.Name == ".." {
return
}
entry.Name = enc.ToStandardName(entry.Name)
entry.Target = enc.ToStandardPath(entry.Target)
}
// dirFromStandardPath returns dir in encoded form.
func dirFromStandardPath(dir string) string {
// Skip . and .. as we don't want these encoded
if dir == "." || dir == ".." {
return dir
}
return enc.FromStandardPath(dir)
}
// findItem finds a directory entry for the name in its parent directory // findItem finds a directory entry for the name in its parent directory
func (f *Fs) findItem(remote string) (entry *ftp.Entry, err error) { func (f *Fs) findItem(remote string) (entry *ftp.Entry, err error) {
// defer fs.Trace(remote, "")("o=%v, err=%v", &o, &err) // defer fs.Trace(remote, "")("o=%v, err=%v", &o, &err)
@ -314,12 +336,13 @@ func (f *Fs) findItem(remote string) (entry *ftp.Entry, err error) {
if err != nil { if err != nil {
return nil, errors.Wrap(err, "findItem") return nil, errors.Wrap(err, "findItem")
} }
files, err := c.List(dir) files, err := c.List(dirFromStandardPath(dir))
f.putFtpConnection(&c, err) f.putFtpConnection(&c, err)
if err != nil { if err != nil {
return nil, translateErrorFile(err) return nil, translateErrorFile(err)
} }
for _, file := range files { for _, file := range files {
entryToStandard(file)
if file.Name == base { if file.Name == base {
return file, nil return file, nil
} }
@ -386,7 +409,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
resultchan := make(chan []*ftp.Entry, 1) resultchan := make(chan []*ftp.Entry, 1)
errchan := make(chan error, 1) errchan := make(chan error, 1)
go func() { go func() {
result, err := c.List(path.Join(f.root, dir)) result, err := c.List(dirFromStandardPath(path.Join(f.root, dir)))
f.putFtpConnection(&c, err) f.putFtpConnection(&c, err)
if err != nil { if err != nil {
errchan <- err errchan <- err
@ -423,6 +446,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
} }
for i := range files { for i := range files {
object := files[i] object := files[i]
entryToStandard(object)
newremote := path.Join(dir, object.Name) newremote := path.Join(dir, object.Name)
switch object.Type { switch object.Type {
case ftp.EntryTypeFolder: case ftp.EntryTypeFolder:
@ -492,19 +516,21 @@ func (f *Fs) getInfo(remote string) (fi *FileInfo, err error) {
if err != nil { if err != nil {
return nil, errors.Wrap(err, "getInfo") return nil, errors.Wrap(err, "getInfo")
} }
files, err := c.List(dir) files, err := c.List(dirFromStandardPath(dir))
f.putFtpConnection(&c, err) f.putFtpConnection(&c, err)
if err != nil { if err != nil {
return nil, translateErrorFile(err) return nil, translateErrorFile(err)
} }
for i := range files { for i := range files {
if files[i].Name == base { file := files[i]
entryToStandard(file)
if file.Name == base {
info := &FileInfo{ info := &FileInfo{
Name: remote, Name: remote,
Size: files[i].Size, Size: file.Size,
ModTime: files[i].Time, ModTime: file.Time,
IsDir: files[i].Type == ftp.EntryTypeFolder, IsDir: file.Type == ftp.EntryTypeFolder,
} }
return info, nil return info, nil
} }
@ -514,6 +540,7 @@ func (f *Fs) getInfo(remote string) (fi *FileInfo, err error) {
// mkdir makes the directory and parents using unrooted paths // mkdir makes the directory and parents using unrooted paths
func (f *Fs) mkdir(abspath string) error { func (f *Fs) mkdir(abspath string) error {
abspath = path.Clean(abspath)
if abspath == "." || abspath == "/" { if abspath == "." || abspath == "/" {
return nil return nil
} }
@ -535,7 +562,7 @@ func (f *Fs) mkdir(abspath string) error {
if connErr != nil { if connErr != nil {
return errors.Wrap(connErr, "mkdir") return errors.Wrap(connErr, "mkdir")
} }
err = c.MakeDir(abspath) err = c.MakeDir(dirFromStandardPath(abspath))
f.putFtpConnection(&c, err) f.putFtpConnection(&c, err)
switch errX := err.(type) { switch errX := err.(type) {
case *textproto.Error: case *textproto.Error:
@ -571,7 +598,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
if err != nil { if err != nil {
return errors.Wrap(translateErrorFile(err), "Rmdir") return errors.Wrap(translateErrorFile(err), "Rmdir")
} }
err = c.RemoveDir(path.Join(f.root, dir)) err = c.RemoveDir(dirFromStandardPath(path.Join(f.root, dir)))
f.putFtpConnection(&c, err) f.putFtpConnection(&c, err)
return translateErrorDir(err) return translateErrorDir(err)
} }
@ -592,8 +619,8 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object,
return nil, errors.Wrap(err, "Move") return nil, errors.Wrap(err, "Move")
} }
err = c.Rename( err = c.Rename(
path.Join(srcObj.fs.root, srcObj.remote), enc.FromStandardPath(path.Join(srcObj.fs.root, srcObj.remote)),
path.Join(f.root, remote), enc.FromStandardPath(path.Join(f.root, remote)),
) )
f.putFtpConnection(&c, err) f.putFtpConnection(&c, err)
if err != nil { if err != nil {
@ -646,8 +673,8 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string
return errors.Wrap(err, "DirMove") return errors.Wrap(err, "DirMove")
} }
err = c.Rename( err = c.Rename(
srcPath, dirFromStandardPath(srcPath),
dstPath, dirFromStandardPath(dstPath),
) )
f.putFtpConnection(&c, err) f.putFtpConnection(&c, err)
if err != nil { if err != nil {
@ -773,7 +800,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (rc io.Read
if err != nil { if err != nil {
return nil, errors.Wrap(err, "open") return nil, errors.Wrap(err, "open")
} }
fd, err := c.RetrFrom(path, uint64(offset)) fd, err := c.RetrFrom(enc.FromStandardPath(path), uint64(offset))
if err != nil { if err != nil {
o.fs.putFtpConnection(&c, err) o.fs.putFtpConnection(&c, err)
return nil, errors.Wrap(err, "open") return nil, errors.Wrap(err, "open")
@ -808,7 +835,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
if err != nil { if err != nil {
return errors.Wrap(err, "Update") return errors.Wrap(err, "Update")
} }
err = c.Stor(path, in) err = c.Stor(enc.FromStandardPath(path), in)
if err != nil { if err != nil {
_ = c.Quit() // toss this connection to avoid sync errors _ = c.Quit() // toss this connection to avoid sync errors
remove() remove()
@ -838,7 +865,7 @@ func (o *Object) Remove(ctx context.Context) (err error) {
if err != nil { if err != nil {
return errors.Wrap(err, "Remove") return errors.Wrap(err, "Remove")
} }
err = c.Delete(path) err = c.Delete(enc.FromStandardPath(path))
o.fs.putFtpConnection(&c, err) o.fs.putFtpConnection(&c, err)
} }
return err return err

View File

@ -103,6 +103,25 @@ will be time of upload.
FTP does not support any checksums. FTP does not support any checksums.
#### Restricted filename characters
In addition to the [default restricted characters set](/overview/#restricted-characters)
the following characters are also replaced:
File names can also not end with the following characters.
These only get replaced if they are last character in the name:
| Character | Value | Replacement |
| --------- |:-----:|:-----------:|
| SP | 0x20 | ␠ |
Note that not all FTP servers can have all characters in file names, for example:
| FTP Server| Forbidden characters |
| --------- |:--------------------:|
| proftpd | `*` |
| pureftpd | `\ [ ]` |
### Implicit TLS ### ### Implicit TLS ###
FTP supports implicit FTP over TLS servers (FTPS). This has to be enabled FTP supports implicit FTP over TLS servers (FTPS). This has to be enabled

View File

@ -222,6 +222,17 @@ const Pcloud = encoder.MultiEncoder(
encoder.EncodeBackSlash | encoder.EncodeBackSlash |
encoder.EncodeInvalidUtf8) encoder.EncodeInvalidUtf8)
// FTP is the encoding used by the ftp backend
//
// The FTP protocal can't handle trailing spaces (for instance
// pureftpd turns them into _)
//
// proftpd can't handle '*' in file names
// pureftpd can't handle '[', ']' or '*'
const FTP = encoder.MultiEncoder(
uint(Display) |
encoder.EncodeRightSpace)
// ByName returns the encoder for a give backend name or nil // ByName returns the encoder for a give backend name or nil
func ByName(name string) encoder.Encoder { func ByName(name string) encoder.Encoder {
switch strings.ToLower(name) { switch strings.ToLower(name) {

View File

@ -19,6 +19,7 @@ const (
Box = Base Box = Base
Drive = Base Drive = Base
Dropbox = Base Dropbox = Base
FTP = Base
GoogleCloudStorage = Base GoogleCloudStorage = Base
JottaCloud = Base JottaCloud = Base
Koofr = Base Koofr = Base