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:
parent
ced2616da5
commit
32af4cd6f3
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user