diff --git a/backend/yandex/yandex.go b/backend/yandex/yandex.go index 3a4ba0ee3..edab61690 100644 --- a/backend/yandex/yandex.go +++ b/backend/yandex/yandex.go @@ -20,6 +20,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/hash" "github.com/rclone/rclone/lib/oauthutil" @@ -29,6 +30,8 @@ import ( "golang.org/x/oauth2" ) +const enc = encodings.Yandex + //oAuth const ( rcloneClientID = "ac39b43b9eba4cae8ffb788c06d816a8" @@ -207,7 +210,7 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string, options *api. Parameters: url.Values{}, } - opts.Parameters.Set("path", path) + opts.Parameters.Set("path", enc.FromStandardPath(path)) if options.SortMode != nil { opts.Parameters.Set("sort", options.SortMode.String()) @@ -234,6 +237,7 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string, options *api. return nil, err } + info.Name = enc.ToStandardName(info.Name) return &info, nil } @@ -360,6 +364,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e if info.ResourceType == "dir" { //list all subdirs for _, element := range info.Embedded.Items { + element.Name = enc.ToStandardName(element.Name) remote := path.Join(dir, element.Name) entry, err := f.itemToDirEntry(ctx, remote, &element) if err != nil { @@ -458,14 +463,18 @@ func (f *Fs) CreateDir(ctx context.Context, path string) (err error) { NoResponse: true, } - opts.Parameters.Set("path", path) + // If creating a directory with a : use (undocumented) disk: prefix + if strings.IndexRune(path, ':') >= 0 { + path = "disk:" + path + } + opts.Parameters.Set("path", enc.FromStandardPath(path)) err = f.pacer.Call(func() (bool, error) { resp, err = f.srv.Call(ctx, &opts) return shouldRetry(resp, err) }) if err != nil { - //fmt.Printf("CreateDir Error: %s\n", err.Error()) + // fmt.Printf("CreateDir %q Error: %s\n", path, err.Error()) return err } // fmt.Printf("...Id %q\n", *info.Id) @@ -572,7 +581,7 @@ func (f *Fs) delete(ctx context.Context, path string, hardDelete bool) (err erro Parameters: url.Values{}, } - opts.Parameters.Set("path", path) + opts.Parameters.Set("path", enc.FromStandardPath(path)) opts.Parameters.Set("permanently", strconv.FormatBool(hardDelete)) var resp *http.Response @@ -644,8 +653,8 @@ func (f *Fs) copyOrMove(ctx context.Context, method, src, dst string, overwrite Parameters: url.Values{}, } - opts.Parameters.Set("from", src) - opts.Parameters.Set("path", dst) + opts.Parameters.Set("from", enc.FromStandardPath(src)) + opts.Parameters.Set("path", enc.FromStandardPath(dst)) opts.Parameters.Set("overwrite", strconv.FormatBool(overwrite)) var resp *http.Response @@ -794,12 +803,12 @@ func (f *Fs) PublicLink(ctx context.Context, remote string) (link string, err er } opts := rest.Opts{ Method: "PUT", - Path: path, + Path: enc.FromStandardPath(path), Parameters: url.Values{}, NoResponse: true, } - opts.Parameters.Set("path", f.filePath(remote)) + opts.Parameters.Set("path", enc.FromStandardPath(f.filePath(remote))) var resp *http.Response err = f.pacer.Call(func() (bool, error) { @@ -985,7 +994,7 @@ func (o *Object) setCustomProperty(ctx context.Context, property string, value s NoResponse: true, } - opts.Parameters.Set("path", o.filePath()) + opts.Parameters.Set("path", enc.FromStandardPath(o.filePath())) rcm := map[string]interface{}{ property: value, } @@ -1022,7 +1031,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read Parameters: url.Values{}, } - opts.Parameters.Set("path", o.filePath()) + opts.Parameters.Set("path", enc.FromStandardPath(o.filePath())) err = o.fs.pacer.Call(func() (bool, error) { resp, err = o.fs.srv.CallJSON(ctx, &opts, nil, &dl) @@ -1059,7 +1068,7 @@ func (o *Object) upload(ctx context.Context, in io.Reader, overwrite bool, mimeT Parameters: url.Values{}, } - opts.Parameters.Set("path", o.filePath()) + opts.Parameters.Set("path", enc.FromStandardPath(o.filePath())) opts.Parameters.Set("overwrite", strconv.FormatBool(overwrite)) err = o.fs.pacer.Call(func() (bool, error) { diff --git a/docs/content/yandex.md b/docs/content/yandex.md index 78c4eb589..bbe57ad9a 100644 --- a/docs/content/yandex.md +++ b/docs/content/yandex.md @@ -9,8 +9,6 @@ date: "2015-12-30" [Yandex Disk](https://disk.yandex.com) is a cloud storage solution created by [Yandex](https://yandex.com). -Yandex paths may be as deep as required, eg `remote:directory/subdirectory`. - Here is an example of making a yandex configuration. First run rclone config @@ -85,6 +83,8 @@ excess files in the path. rclone sync /home/local/directory remote:directory +Yandex paths may be as deep as required, eg `remote:directory/subdirectory`. + ### Modified time ### Modified times are supported and are stored accurate to 1 ns in custom @@ -105,6 +105,14 @@ 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 + +The [default restricted characters set](/overview/#restricted-characters) +are replaced. + +Invalid UTF-8 bytes will also be [replaced](/overview/#invalid-utf8), +as they can't be used in JSON strings. + ### Limitations ### When uploading very large files (bigger than about 5GB) you will need diff --git a/fs/encodings/encodings.go b/fs/encodings/encodings.go index 6764bd3c1..cd23c0b15 100644 --- a/fs/encodings/encodings.go +++ b/fs/encodings/encodings.go @@ -342,6 +342,14 @@ const Sharefile = encoder.MultiEncoder( encoder.EncodeLeftPeriod | encoder.EncodeInvalidUtf8) +// Yandex is the encoding used by the yandex backend +// +// Of the control characters \t \n \r are allowed +// it doesn't seem worth making an exception for this +const Yandex = encoder.MultiEncoder( + uint(Display) | + encoder.EncodeInvalidUtf8) + // ByName returns the encoder for a give backend name or nil func ByName(name string) encoder.Encoder { switch strings.ToLower(name) { @@ -403,7 +411,8 @@ func ByName(name string) encoder.Encoder { case "swift": return Swift //case "webdav": - //case "yandex": + case "yandex": + return Yandex default: return nil } diff --git a/fs/encodings/encodings_noencode.go b/fs/encodings/encodings_noencode.go index e89a4dfaf..932c86aca 100644 --- a/fs/encodings/encodings_noencode.go +++ b/fs/encodings/encodings_noencode.go @@ -36,6 +36,7 @@ const ( S3 = Base Sharefile = Base Swift = Base + Yandex = Base ) // ByName returns the encoder for a give backend name or nil