From 135717e12bf50a8d2aa43c1540e89c6199451d5e Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 2 Oct 2019 19:36:25 +0100 Subject: [PATCH] mailru: use lib/encoder --- backend/mailru/mailru.go | 35 ++++++++++++++++-------------- docs/content/mailru.md | 19 ++++++++++++++++ fs/encodings/encodings.go | 11 ++++++++++ fs/encodings/encodings_noencode.go | 1 + 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/backend/mailru/mailru.go b/backend/mailru/mailru.go index f799aec3e..6afd563bc 100644 --- a/backend/mailru/mailru.go +++ b/backend/mailru/mailru.go @@ -27,6 +27,7 @@ import ( "github.com/rclone/rclone/fs/config/configmap" "github.com/rclone/rclone/fs/config/configstruct" "github.com/rclone/rclone/fs/config/obscure" + "github.com/rclone/rclone/fs/encodings" "github.com/rclone/rclone/fs/fserrors" "github.com/rclone/rclone/fs/fshttp" "github.com/rclone/rclone/fs/hash" @@ -41,6 +42,8 @@ import ( "golang.org/x/oauth2" ) +const enc = encodings.Mailru + // Global constants const ( minSleepPacer = 10 * time.Millisecond @@ -519,7 +522,7 @@ func (f *Fs) accessToken() (string, error) { // absPath converts root-relative remote to absolute home path func (f *Fs) absPath(remote string) string { - return "/" + path.Join(f.root, strings.Trim(remote, "/")) + return path.Join("/", f.root, remote) } // relPath converts absolute home path to root-relative remote @@ -604,7 +607,7 @@ func (f *Fs) readItemMetaData(ctx context.Context, path string) (entry fs.DirEnt Path: "/api/m1/file", Parameters: url.Values{ "access_token": {token}, - "home": {path}, + "home": {enc.FromStandardPath(path)}, "offset": {"0"}, "limit": {strconv.Itoa(maxInt32)}, }, @@ -639,7 +642,7 @@ func (f *Fs) readItemMetaData(ctx context.Context, path string) (entry fs.DirEnt // =0 - for an empty directory // >0 - for a non-empty directory func (f *Fs) itemToDirEntry(ctx context.Context, item *api.ListItem) (entry fs.DirEntry, dirSize int, err error) { - remote, err := f.relPath(item.Home) + remote, err := f.relPath(enc.ToStandardPath(item.Home)) if err != nil { return nil, -1, err } @@ -705,7 +708,7 @@ func (f *Fs) listM1(ctx context.Context, dirPath string, offset int, limit int) params.Set("limit", strconv.Itoa(limit)) data := url.Values{} - data.Set("home", dirPath) + data.Set("home", enc.FromStandardPath(dirPath)) opts := rest.Opts{ Method: "POST", @@ -753,7 +756,7 @@ func (f *Fs) listBin(ctx context.Context, dirPath string, depth int) (entries fs req := api.NewBinWriter() req.WritePu16(api.OperationFolderList) - req.WriteString(dirPath) + req.WriteString(enc.FromStandardPath(dirPath)) req.WritePu32(int64(depth)) req.WritePu32(int64(options)) req.WritePu32(0) @@ -889,7 +892,7 @@ func (t *treeState) NextRecord() (fs.DirEntry, error) { if (head & 4096) != 0 { t.dunnoNodeID = r.ReadNBytes(api.DunnoNodeIDLength) } - name := string(r.ReadBytesByLength()) + name := enc.FromStandardPath(string(r.ReadBytesByLength())) t.dunno1 = int(r.ReadULong()) t.dunno2 = 0 t.dunno3 = 0 @@ -1028,7 +1031,7 @@ func (f *Fs) CreateDir(ctx context.Context, path string) error { req := api.NewBinWriter() req.WritePu16(api.OperationCreateFolder) req.WritePu16(0) // revision - req.WriteString(path) + req.WriteString(enc.FromStandardPath(path)) req.WritePu32(0) token, err := f.accessToken() @@ -1183,7 +1186,7 @@ func (f *Fs) delete(ctx context.Context, path string, hardDelete bool) error { return err } - data := url.Values{"home": {path}} + data := url.Values{"home": {enc.FromStandardPath(path)}} opts := rest.Opts{ Method: "POST", Path: "/api/m1/file/remove", @@ -1240,8 +1243,8 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, } data := url.Values{} - data.Set("home", srcPath) - data.Set("folder", parentDir(dstPath)) + data.Set("home", enc.FromStandardPath(srcPath)) + data.Set("folder", enc.FromStandardPath(parentDir(dstPath))) data.Set("email", f.opt.Username) data.Set("x-email", f.opt.Username) @@ -1279,7 +1282,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, return nil, fmt.Errorf("copy failed with code %d", response.Status) } - tmpPath := response.Body + tmpPath := enc.ToStandardPath(response.Body) if tmpPath != dstPath { fs.Debugf(f, "rename temporary file %q -> %q\n", tmpPath, dstPath) err = f.moveItemBin(ctx, tmpPath, dstPath, "rename temporary file") @@ -1354,9 +1357,9 @@ func (f *Fs) moveItemBin(ctx context.Context, srcPath, dstPath, opName string) e req := api.NewBinWriter() req.WritePu16(api.OperationRename) req.WritePu32(0) // old revision - req.WriteString(srcPath) + req.WriteString(enc.FromStandardPath(srcPath)) req.WritePu32(0) // new revision - req.WriteString(dstPath) + req.WriteString(enc.FromStandardPath(dstPath)) req.WritePu32(0) // dunno opts := rest.Opts{ @@ -1447,7 +1450,7 @@ func (f *Fs) PublicLink(ctx context.Context, remote string) (link string, err er } data := url.Values{} - data.Set("home", f.absPath(remote)) + data.Set("home", enc.FromStandardPath(f.absPath(remote))) data.Set("email", f.opt.Username) data.Set("x-email", f.opt.Username) @@ -2012,7 +2015,7 @@ func (o *Object) addFileMetaData(ctx context.Context, overwrite bool) error { req := api.NewBinWriter() req.WritePu16(api.OperationAddFile) req.WritePu16(0) // revision - req.WriteString(o.absPath()) + req.WriteString(enc.FromStandardPath(o.absPath())) req.WritePu64(o.size) req.WritePu64(o.modTime.Unix()) req.WritePu32(0) @@ -2110,7 +2113,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read opts := rest.Opts{ Method: "GET", Options: options, - Path: url.PathEscape(strings.TrimLeft(o.absPath(), "/")), + Path: url.PathEscape(strings.TrimLeft(enc.FromStandardPath(o.absPath()), "/")), Parameters: url.Values{ "client_id": {api.OAuthClientID}, "token": {token}, diff --git a/docs/content/mailru.md b/docs/content/mailru.md index 63e612a70..a90f4f23f 100644 --- a/docs/content/mailru.md +++ b/docs/content/mailru.md @@ -134,6 +134,25 @@ This command does not take any path arguments. To view your current quota you can use the `rclone about remote:` command which will display your usage limit (quota) and the current usage. +#### Restricted filename characters + +In addition to the [default restricted characters set](/overview/#restricted-characters) +the following characters are also replaced: + +| Character | Value | Replacement | +| --------- |:-----:|:-----------:| +| " | 0x22 | " | +| * | 0x2A | * | +| : | 0x3A | : | +| < | 0x3C | < | +| > | 0x3E | > | +| ? | 0x3F | ? | +| \ | 0x5C | \ | +| \| | 0x7C | | | + +Invalid UTF-8 bytes will also be [replaced](/overview/#invalid-utf8), +as they can't be used in JSON strings. + ### Limitations ### File size limits depend on your account. A single file size is limited by 2G diff --git a/fs/encodings/encodings.go b/fs/encodings/encodings.go index d28c70c03..6764bd3c1 100644 --- a/fs/encodings/encodings.go +++ b/fs/encodings/encodings.go @@ -133,6 +133,15 @@ const Koofr = encoder.MultiEncoder( encoder.EncodeBackSlash | encoder.EncodeInvalidUtf8) +// Mailru is the encoding used by the mailru backend +// +// Encode invalid UTF-8 bytes as json doesn't handle them properly. +const Mailru = encoder.MultiEncoder( + uint(Display) | + encoder.EncodeWin | // :?"*<>| + encoder.EncodeBackSlash | + encoder.EncodeInvalidUtf8) + // Mega is the encoding used by the mega backend // // Encode invalid UTF-8 bytes as json doesn't handle them properly. @@ -372,6 +381,8 @@ func ByName(name string) encoder.Encoder { return LocalUnix case "local-macos", "macos": return LocalMacOS + case "mailru": + return Mailru case "mega": return Mega case "onedrive": diff --git a/fs/encodings/encodings_noencode.go b/fs/encodings/encodings_noencode.go index 6f29e5966..e89a4dfaf 100644 --- a/fs/encodings/encodings_noencode.go +++ b/fs/encodings/encodings_noencode.go @@ -25,6 +25,7 @@ const ( GoogleCloudStorage = Base JottaCloud = Base Koofr = Base + Mailru = Base Mega = Base OneDrive = Base OpenDrive = Base