mailru: use lib/encoder

This commit is contained in:
Nick Craig-Wood 2019-10-02 19:36:25 +01:00
parent 6b55b8b133
commit 135717e12b
4 changed files with 50 additions and 16 deletions

View File

@ -27,6 +27,7 @@ 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/fserrors" "github.com/rclone/rclone/fs/fserrors"
"github.com/rclone/rclone/fs/fshttp" "github.com/rclone/rclone/fs/fshttp"
"github.com/rclone/rclone/fs/hash" "github.com/rclone/rclone/fs/hash"
@ -41,6 +42,8 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
const enc = encodings.Mailru
// Global constants // Global constants
const ( const (
minSleepPacer = 10 * time.Millisecond minSleepPacer = 10 * time.Millisecond
@ -519,7 +522,7 @@ func (f *Fs) accessToken() (string, error) {
// absPath converts root-relative remote to absolute home path // absPath converts root-relative remote to absolute home path
func (f *Fs) absPath(remote string) string { 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 // 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", Path: "/api/m1/file",
Parameters: url.Values{ Parameters: url.Values{
"access_token": {token}, "access_token": {token},
"home": {path}, "home": {enc.FromStandardPath(path)},
"offset": {"0"}, "offset": {"0"},
"limit": {strconv.Itoa(maxInt32)}, "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 an empty directory
// >0 - for a non-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) { 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 { if err != nil {
return nil, -1, err 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)) params.Set("limit", strconv.Itoa(limit))
data := url.Values{} data := url.Values{}
data.Set("home", dirPath) data.Set("home", enc.FromStandardPath(dirPath))
opts := rest.Opts{ opts := rest.Opts{
Method: "POST", Method: "POST",
@ -753,7 +756,7 @@ func (f *Fs) listBin(ctx context.Context, dirPath string, depth int) (entries fs
req := api.NewBinWriter() req := api.NewBinWriter()
req.WritePu16(api.OperationFolderList) req.WritePu16(api.OperationFolderList)
req.WriteString(dirPath) req.WriteString(enc.FromStandardPath(dirPath))
req.WritePu32(int64(depth)) req.WritePu32(int64(depth))
req.WritePu32(int64(options)) req.WritePu32(int64(options))
req.WritePu32(0) req.WritePu32(0)
@ -889,7 +892,7 @@ func (t *treeState) NextRecord() (fs.DirEntry, error) {
if (head & 4096) != 0 { if (head & 4096) != 0 {
t.dunnoNodeID = r.ReadNBytes(api.DunnoNodeIDLength) t.dunnoNodeID = r.ReadNBytes(api.DunnoNodeIDLength)
} }
name := string(r.ReadBytesByLength()) name := enc.FromStandardPath(string(r.ReadBytesByLength()))
t.dunno1 = int(r.ReadULong()) t.dunno1 = int(r.ReadULong())
t.dunno2 = 0 t.dunno2 = 0
t.dunno3 = 0 t.dunno3 = 0
@ -1028,7 +1031,7 @@ func (f *Fs) CreateDir(ctx context.Context, path string) error {
req := api.NewBinWriter() req := api.NewBinWriter()
req.WritePu16(api.OperationCreateFolder) req.WritePu16(api.OperationCreateFolder)
req.WritePu16(0) // revision req.WritePu16(0) // revision
req.WriteString(path) req.WriteString(enc.FromStandardPath(path))
req.WritePu32(0) req.WritePu32(0)
token, err := f.accessToken() token, err := f.accessToken()
@ -1183,7 +1186,7 @@ func (f *Fs) delete(ctx context.Context, path string, hardDelete bool) error {
return err return err
} }
data := url.Values{"home": {path}} data := url.Values{"home": {enc.FromStandardPath(path)}}
opts := rest.Opts{ opts := rest.Opts{
Method: "POST", Method: "POST",
Path: "/api/m1/file/remove", 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 := url.Values{}
data.Set("home", srcPath) data.Set("home", enc.FromStandardPath(srcPath))
data.Set("folder", parentDir(dstPath)) data.Set("folder", enc.FromStandardPath(parentDir(dstPath)))
data.Set("email", f.opt.Username) data.Set("email", f.opt.Username)
data.Set("x-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) return nil, fmt.Errorf("copy failed with code %d", response.Status)
} }
tmpPath := response.Body tmpPath := enc.ToStandardPath(response.Body)
if tmpPath != dstPath { if tmpPath != dstPath {
fs.Debugf(f, "rename temporary file %q -> %q\n", tmpPath, dstPath) fs.Debugf(f, "rename temporary file %q -> %q\n", tmpPath, dstPath)
err = f.moveItemBin(ctx, tmpPath, dstPath, "rename temporary file") 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 := api.NewBinWriter()
req.WritePu16(api.OperationRename) req.WritePu16(api.OperationRename)
req.WritePu32(0) // old revision req.WritePu32(0) // old revision
req.WriteString(srcPath) req.WriteString(enc.FromStandardPath(srcPath))
req.WritePu32(0) // new revision req.WritePu32(0) // new revision
req.WriteString(dstPath) req.WriteString(enc.FromStandardPath(dstPath))
req.WritePu32(0) // dunno req.WritePu32(0) // dunno
opts := rest.Opts{ opts := rest.Opts{
@ -1447,7 +1450,7 @@ func (f *Fs) PublicLink(ctx context.Context, remote string) (link string, err er
} }
data := url.Values{} 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("email", f.opt.Username)
data.Set("x-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 := api.NewBinWriter()
req.WritePu16(api.OperationAddFile) req.WritePu16(api.OperationAddFile)
req.WritePu16(0) // revision req.WritePu16(0) // revision
req.WriteString(o.absPath()) req.WriteString(enc.FromStandardPath(o.absPath()))
req.WritePu64(o.size) req.WritePu64(o.size)
req.WritePu64(o.modTime.Unix()) req.WritePu64(o.modTime.Unix())
req.WritePu32(0) req.WritePu32(0)
@ -2110,7 +2113,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read
opts := rest.Opts{ opts := rest.Opts{
Method: "GET", Method: "GET",
Options: options, Options: options,
Path: url.PathEscape(strings.TrimLeft(o.absPath(), "/")), Path: url.PathEscape(strings.TrimLeft(enc.FromStandardPath(o.absPath()), "/")),
Parameters: url.Values{ Parameters: url.Values{
"client_id": {api.OAuthClientID}, "client_id": {api.OAuthClientID},
"token": {token}, "token": {token},

View File

@ -134,6 +134,25 @@ This command does not take any path arguments.
To view your current quota you can use the `rclone about remote:` To view your current quota you can use the `rclone about remote:`
command which will display your usage limit (quota) and the current usage. 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 ### ### Limitations ###
File size limits depend on your account. A single file size is limited by 2G File size limits depend on your account. A single file size is limited by 2G

View File

@ -133,6 +133,15 @@ const Koofr = encoder.MultiEncoder(
encoder.EncodeBackSlash | encoder.EncodeBackSlash |
encoder.EncodeInvalidUtf8) 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 // Mega is the encoding used by the mega backend
// //
// Encode invalid UTF-8 bytes as json doesn't handle them properly. // Encode invalid UTF-8 bytes as json doesn't handle them properly.
@ -372,6 +381,8 @@ func ByName(name string) encoder.Encoder {
return LocalUnix return LocalUnix
case "local-macos", "macos": case "local-macos", "macos":
return LocalMacOS return LocalMacOS
case "mailru":
return Mailru
case "mega": case "mega":
return Mega return Mega
case "onedrive": case "onedrive":

View File

@ -25,6 +25,7 @@ const (
GoogleCloudStorage = Base GoogleCloudStorage = Base
JottaCloud = Base JottaCloud = Base
Koofr = Base Koofr = Base
Mailru = Base
Mega = Base Mega = Base
OneDrive = Base OneDrive = Base
OpenDrive = Base OpenDrive = Base