forked from TrueCloudLab/rclone
backend: adjust backends to have encoding parameter
Fixes #3761 Fixes #3836 Fixes #3841
This commit is contained in:
parent
0a5c83ece1
commit
3c620d521d
35 changed files with 667 additions and 514 deletions
|
@ -33,13 +33,13 @@ import (
|
||||||
"github.com/rclone/rclone/fs/fshttp"
|
"github.com/rclone/rclone/fs/fshttp"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
enc = encodings.AmazonCloudDrive
|
|
||||||
folderKind = "FOLDER"
|
folderKind = "FOLDER"
|
||||||
fileKind = "FILE"
|
fileKind = "FILE"
|
||||||
statusAvailable = "AVAILABLE"
|
statusAvailable = "AVAILABLE"
|
||||||
|
@ -137,6 +137,11 @@ which downloads the file through a temporary URL directly from the
|
||||||
underlying S3 storage.`,
|
underlying S3 storage.`,
|
||||||
Default: defaultTempLinkThreshold,
|
Default: defaultTempLinkThreshold,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.AmazonCloudDrive,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -146,6 +151,7 @@ type Options struct {
|
||||||
Checkpoint string `config:"checkpoint"`
|
Checkpoint string `config:"checkpoint"`
|
||||||
UploadWaitPerGB fs.Duration `config:"upload_wait_per_gb"`
|
UploadWaitPerGB fs.Duration `config:"upload_wait_per_gb"`
|
||||||
TempLinkThreshold fs.SizeSuffix `config:"templink_threshold"`
|
TempLinkThreshold fs.SizeSuffix `config:"templink_threshold"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote acd server
|
// Fs represents a remote acd server
|
||||||
|
@ -386,7 +392,7 @@ func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut strin
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
var subFolder *acd.Folder
|
var subFolder *acd.Folder
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
subFolder, resp, err = folder.GetFolder(enc.FromStandardName(leaf))
|
subFolder, resp, err = folder.GetFolder(f.opt.Enc.FromStandardName(leaf))
|
||||||
return f.shouldRetry(resp, err)
|
return f.shouldRetry(resp, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -413,7 +419,7 @@ func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string,
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
var info *acd.Folder
|
var info *acd.Folder
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
info, resp, err = folder.CreateFolder(enc.FromStandardName(leaf))
|
info, resp, err = folder.CreateFolder(f.opt.Enc.FromStandardName(leaf))
|
||||||
return f.shouldRetry(resp, err)
|
return f.shouldRetry(resp, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -481,7 +487,7 @@ func (f *Fs) listAll(dirID string, title string, directoriesOnly bool, filesOnly
|
||||||
if !hasValidParent {
|
if !hasValidParent {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
*node.Name = enc.ToStandardName(*node.Name)
|
*node.Name = f.opt.Enc.ToStandardName(*node.Name)
|
||||||
// Store the nodes up in case we have to retry the listing
|
// Store the nodes up in case we have to retry the listing
|
||||||
out = append(out, node)
|
out = append(out, node)
|
||||||
}
|
}
|
||||||
|
@ -671,7 +677,7 @@ func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options .
|
||||||
err = f.pacer.CallNoRetry(func() (bool, error) {
|
err = f.pacer.CallNoRetry(func() (bool, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
f.tokenRenewer.Start()
|
f.tokenRenewer.Start()
|
||||||
info, resp, err = folder.Put(in, enc.FromStandardName(leaf))
|
info, resp, err = folder.Put(in, f.opt.Enc.FromStandardName(leaf))
|
||||||
f.tokenRenewer.Stop()
|
f.tokenRenewer.Stop()
|
||||||
var ok bool
|
var ok bool
|
||||||
ok, info, err = f.checkUpload(ctx, resp, in, src, info, err, time.Since(start))
|
ok, info, err = f.checkUpload(ctx, resp, in, src, info, err, time.Since(start))
|
||||||
|
@ -1041,7 +1047,7 @@ func (o *Object) readMetaData(ctx context.Context) (err error) {
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
var info *acd.File
|
var info *acd.File
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
info, resp, err = folder.GetFile(enc.FromStandardName(leaf))
|
info, resp, err = folder.GetFile(o.fs.opt.Enc.FromStandardName(leaf))
|
||||||
return o.fs.shouldRetry(resp, err)
|
return o.fs.shouldRetry(resp, err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1161,7 +1167,7 @@ func (f *Fs) restoreNode(info *acd.Node) (newInfo *acd.Node, err error) {
|
||||||
func (f *Fs) renameNode(info *acd.Node, newName string) (newInfo *acd.Node, err error) {
|
func (f *Fs) renameNode(info *acd.Node, newName string) (newInfo *acd.Node, err error) {
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
newInfo, resp, err = info.Rename(enc.FromStandardName(newName))
|
newInfo, resp, err = info.Rename(f.opt.Enc.FromStandardName(newName))
|
||||||
return f.shouldRetry(resp, err)
|
return f.shouldRetry(resp, err)
|
||||||
})
|
})
|
||||||
return newInfo, err
|
return newInfo, err
|
||||||
|
@ -1357,7 +1363,7 @@ func (f *Fs) changeNotifyRunner(notifyFunc func(string, fs.EntryType), checkpoin
|
||||||
if len(node.Parents) > 0 {
|
if len(node.Parents) > 0 {
|
||||||
if path, ok := f.dirCache.GetInv(node.Parents[0]); ok {
|
if path, ok := f.dirCache.GetInv(node.Parents[0]); ok {
|
||||||
// and append the drive file name to compute the full file name
|
// and append the drive file name to compute the full file name
|
||||||
name := enc.ToStandardName(*node.Name)
|
name := f.opt.Enc.ToStandardName(*node.Name)
|
||||||
if len(path) > 0 {
|
if len(path) > 0 {
|
||||||
path = path + "/" + name
|
path = path + "/" + name
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
"github.com/rclone/rclone/fs/accounting"
|
"github.com/rclone/rclone/fs/accounting"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
|
@ -34,6 +35,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/fs/walk"
|
"github.com/rclone/rclone/fs/walk"
|
||||||
"github.com/rclone/rclone/lib/bucket"
|
"github.com/rclone/rclone/lib/bucket"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -61,8 +63,6 @@ const (
|
||||||
emulatorBlobEndpoint = "http://127.0.0.1:10000/devstoreaccount1"
|
emulatorBlobEndpoint = "http://127.0.0.1:10000/devstoreaccount1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.AzureBlob
|
|
||||||
|
|
||||||
// Register with Fs
|
// Register with Fs
|
||||||
func init() {
|
func init() {
|
||||||
fs.Register(&fs.RegInfo{
|
fs.Register(&fs.RegInfo{
|
||||||
|
@ -127,6 +127,11 @@ If blobs are in "archive tier" at remote, trying to perform data transfer
|
||||||
operations from remote will not be allowed. User should first restore by
|
operations from remote will not be allowed. User should first restore by
|
||||||
tiering blob to "Hot" or "Cool".`,
|
tiering blob to "Hot" or "Cool".`,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.AzureBlob,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -142,6 +147,7 @@ type Options struct {
|
||||||
ListChunkSize uint `config:"list_chunk"`
|
ListChunkSize uint `config:"list_chunk"`
|
||||||
AccessTier string `config:"access_tier"`
|
AccessTier string `config:"access_tier"`
|
||||||
UseEmulator bool `config:"use_emulator"`
|
UseEmulator bool `config:"use_emulator"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote azure server
|
// Fs represents a remote azure server
|
||||||
|
@ -212,7 +218,7 @@ func parsePath(path string) (root string) {
|
||||||
// relative to f.root
|
// relative to f.root
|
||||||
func (f *Fs) split(rootRelativePath string) (containerName, containerPath string) {
|
func (f *Fs) split(rootRelativePath string) (containerName, containerPath string) {
|
||||||
containerName, containerPath = bucket.Split(path.Join(f.root, rootRelativePath))
|
containerName, containerPath = bucket.Split(path.Join(f.root, rootRelativePath))
|
||||||
return enc.FromStandardName(containerName), enc.FromStandardPath(containerPath)
|
return f.opt.Enc.FromStandardName(containerName), f.opt.Enc.FromStandardPath(containerPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// split returns container and containerPath from the object
|
// split returns container and containerPath from the object
|
||||||
|
@ -588,7 +594,7 @@ func (f *Fs) list(ctx context.Context, container, directory, prefix string, addC
|
||||||
// if prefix != "" && !strings.HasPrefix(file.Name, prefix) {
|
// if prefix != "" && !strings.HasPrefix(file.Name, prefix) {
|
||||||
// return nil
|
// return nil
|
||||||
// }
|
// }
|
||||||
remote := enc.ToStandardPath(file.Name)
|
remote := f.opt.Enc.ToStandardPath(file.Name)
|
||||||
if !strings.HasPrefix(remote, prefix) {
|
if !strings.HasPrefix(remote, prefix) {
|
||||||
fs.Debugf(f, "Odd name received %q", remote)
|
fs.Debugf(f, "Odd name received %q", remote)
|
||||||
continue
|
continue
|
||||||
|
@ -609,7 +615,7 @@ func (f *Fs) list(ctx context.Context, container, directory, prefix string, addC
|
||||||
// Send the subdirectories
|
// Send the subdirectories
|
||||||
for _, remote := range response.Segment.BlobPrefixes {
|
for _, remote := range response.Segment.BlobPrefixes {
|
||||||
remote := strings.TrimRight(remote.Name, "/")
|
remote := strings.TrimRight(remote.Name, "/")
|
||||||
remote = enc.ToStandardPath(remote)
|
remote = f.opt.Enc.ToStandardPath(remote)
|
||||||
if !strings.HasPrefix(remote, prefix) {
|
if !strings.HasPrefix(remote, prefix) {
|
||||||
fs.Debugf(f, "Odd directory name received %q", remote)
|
fs.Debugf(f, "Odd directory name received %q", remote)
|
||||||
continue
|
continue
|
||||||
|
@ -673,7 +679,7 @@ func (f *Fs) listContainers(ctx context.Context) (entries fs.DirEntries, err err
|
||||||
return entries, nil
|
return entries, nil
|
||||||
}
|
}
|
||||||
err = f.listContainersToFn(func(container *azblob.ContainerItem) error {
|
err = f.listContainersToFn(func(container *azblob.ContainerItem) error {
|
||||||
d := fs.NewDir(enc.ToStandardName(container.Name), container.Properties.LastModified)
|
d := fs.NewDir(f.opt.Enc.ToStandardName(container.Name), container.Properties.LastModified)
|
||||||
f.cache.MarkOK(container.Name)
|
f.cache.MarkOK(container.Name)
|
||||||
entries = append(entries, d)
|
entries = append(entries, d)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/rclone/rclone/backend/b2/api"
|
"github.com/rclone/rclone/backend/b2/api"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
"github.com/rclone/rclone/fs/accounting"
|
"github.com/rclone/rclone/fs/accounting"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
|
@ -31,12 +32,11 @@ import (
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/fs/walk"
|
"github.com/rclone/rclone/fs/walk"
|
||||||
"github.com/rclone/rclone/lib/bucket"
|
"github.com/rclone/rclone/lib/bucket"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.B2
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultEndpoint = "https://api.backblazeb2.com"
|
defaultEndpoint = "https://api.backblazeb2.com"
|
||||||
headerPrefix = "x-bz-info-" // lower case as that is what the server returns
|
headerPrefix = "x-bz-info-" // lower case as that is what the server returns
|
||||||
|
@ -146,6 +146,11 @@ The duration before the download authorization token will expire.
|
||||||
The minimum value is 1 second. The maximum value is one week.`,
|
The minimum value is 1 second. The maximum value is one week.`,
|
||||||
Default: fs.Duration(7 * 24 * time.Hour),
|
Default: fs.Duration(7 * 24 * time.Hour),
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.B2,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -163,6 +168,7 @@ type Options struct {
|
||||||
DisableCheckSum bool `config:"disable_checksum"`
|
DisableCheckSum bool `config:"disable_checksum"`
|
||||||
DownloadURL string `config:"download_url"`
|
DownloadURL string `config:"download_url"`
|
||||||
DownloadAuthorizationDuration fs.Duration `config:"download_auth_duration"`
|
DownloadAuthorizationDuration fs.Duration `config:"download_auth_duration"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote b2 server
|
// Fs represents a remote b2 server
|
||||||
|
@ -402,7 +408,7 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
|
||||||
}
|
}
|
||||||
// If this is a key limited to a single bucket, it must exist already
|
// If this is a key limited to a single bucket, it must exist already
|
||||||
if f.rootBucket != "" && f.info.Allowed.BucketID != "" {
|
if f.rootBucket != "" && f.info.Allowed.BucketID != "" {
|
||||||
allowedBucket := enc.ToStandardName(f.info.Allowed.BucketName)
|
allowedBucket := f.opt.Enc.ToStandardName(f.info.Allowed.BucketName)
|
||||||
if allowedBucket == "" {
|
if allowedBucket == "" {
|
||||||
return nil, errors.New("bucket that application key is restricted to no longer exists")
|
return nil, errors.New("bucket that application key is restricted to no longer exists")
|
||||||
}
|
}
|
||||||
|
@ -623,11 +629,11 @@ func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBuck
|
||||||
var request = api.ListFileNamesRequest{
|
var request = api.ListFileNamesRequest{
|
||||||
BucketID: bucketID,
|
BucketID: bucketID,
|
||||||
MaxFileCount: chunkSize,
|
MaxFileCount: chunkSize,
|
||||||
Prefix: enc.FromStandardPath(directory),
|
Prefix: f.opt.Enc.FromStandardPath(directory),
|
||||||
Delimiter: delimiter,
|
Delimiter: delimiter,
|
||||||
}
|
}
|
||||||
if directory != "" {
|
if directory != "" {
|
||||||
request.StartFileName = enc.FromStandardPath(directory)
|
request.StartFileName = f.opt.Enc.FromStandardPath(directory)
|
||||||
}
|
}
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
|
@ -647,7 +653,7 @@ func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBuck
|
||||||
}
|
}
|
||||||
for i := range response.Files {
|
for i := range response.Files {
|
||||||
file := &response.Files[i]
|
file := &response.Files[i]
|
||||||
file.Name = enc.ToStandardPath(file.Name)
|
file.Name = f.opt.Enc.ToStandardPath(file.Name)
|
||||||
// Finish if file name no longer has prefix
|
// Finish if file name no longer has prefix
|
||||||
if prefix != "" && !strings.HasPrefix(file.Name, prefix) {
|
if prefix != "" && !strings.HasPrefix(file.Name, prefix) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -848,7 +854,7 @@ func (f *Fs) listBucketsToFn(ctx context.Context, fn listBucketFn) error {
|
||||||
f._bucketType = make(map[string]string, 1)
|
f._bucketType = make(map[string]string, 1)
|
||||||
for i := range response.Buckets {
|
for i := range response.Buckets {
|
||||||
bucket := &response.Buckets[i]
|
bucket := &response.Buckets[i]
|
||||||
bucket.Name = enc.ToStandardName(bucket.Name)
|
bucket.Name = f.opt.Enc.ToStandardName(bucket.Name)
|
||||||
f.cache.MarkOK(bucket.Name)
|
f.cache.MarkOK(bucket.Name)
|
||||||
f._bucketID[bucket.Name] = bucket.ID
|
f._bucketID[bucket.Name] = bucket.ID
|
||||||
f._bucketType[bucket.Name] = bucket.Type
|
f._bucketType[bucket.Name] = bucket.Type
|
||||||
|
@ -970,7 +976,7 @@ func (f *Fs) makeBucket(ctx context.Context, bucket string) error {
|
||||||
}
|
}
|
||||||
var request = api.CreateBucketRequest{
|
var request = api.CreateBucketRequest{
|
||||||
AccountID: f.info.AccountID,
|
AccountID: f.info.AccountID,
|
||||||
Name: enc.FromStandardName(bucket),
|
Name: f.opt.Enc.FromStandardName(bucket),
|
||||||
Type: "allPrivate",
|
Type: "allPrivate",
|
||||||
}
|
}
|
||||||
var response api.Bucket
|
var response api.Bucket
|
||||||
|
@ -1054,7 +1060,7 @@ func (f *Fs) hide(ctx context.Context, bucket, bucketPath string) error {
|
||||||
}
|
}
|
||||||
var request = api.HideFileRequest{
|
var request = api.HideFileRequest{
|
||||||
BucketID: bucketID,
|
BucketID: bucketID,
|
||||||
Name: enc.FromStandardPath(bucketPath),
|
Name: f.opt.Enc.FromStandardPath(bucketPath),
|
||||||
}
|
}
|
||||||
var response api.File
|
var response api.File
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
@ -1082,7 +1088,7 @@ func (f *Fs) deleteByID(ctx context.Context, ID, Name string) error {
|
||||||
}
|
}
|
||||||
var request = api.DeleteFileRequest{
|
var request = api.DeleteFileRequest{
|
||||||
ID: ID,
|
ID: ID,
|
||||||
Name: enc.FromStandardPath(Name),
|
Name: f.opt.Enc.FromStandardPath(Name),
|
||||||
}
|
}
|
||||||
var response api.File
|
var response api.File
|
||||||
err := f.pacer.Call(func() (bool, error) {
|
err := f.pacer.Call(func() (bool, error) {
|
||||||
|
@ -1220,7 +1226,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
}
|
}
|
||||||
var request = api.CopyFileRequest{
|
var request = api.CopyFileRequest{
|
||||||
SourceID: srcObj.id,
|
SourceID: srcObj.id,
|
||||||
Name: enc.FromStandardPath(dstPath),
|
Name: f.opt.Enc.FromStandardPath(dstPath),
|
||||||
MetadataDirective: "COPY",
|
MetadataDirective: "COPY",
|
||||||
DestBucketID: destBucketID,
|
DestBucketID: destBucketID,
|
||||||
}
|
}
|
||||||
|
@ -1268,7 +1274,7 @@ func (f *Fs) getDownloadAuthorization(ctx context.Context, bucket, remote string
|
||||||
}
|
}
|
||||||
var request = api.GetDownloadAuthorizationRequest{
|
var request = api.GetDownloadAuthorizationRequest{
|
||||||
BucketID: bucketID,
|
BucketID: bucketID,
|
||||||
FileNamePrefix: enc.FromStandardPath(path.Join(f.root, remote)),
|
FileNamePrefix: f.opt.Enc.FromStandardPath(path.Join(f.root, remote)),
|
||||||
ValidDurationInSeconds: validDurationInSeconds,
|
ValidDurationInSeconds: validDurationInSeconds,
|
||||||
}
|
}
|
||||||
var response api.GetDownloadAuthorizationResponse
|
var response api.GetDownloadAuthorizationResponse
|
||||||
|
@ -1509,7 +1515,7 @@ func (o *Object) SetModTime(ctx context.Context, modTime time.Time) error {
|
||||||
}
|
}
|
||||||
var request = api.CopyFileRequest{
|
var request = api.CopyFileRequest{
|
||||||
SourceID: o.id,
|
SourceID: o.id,
|
||||||
Name: enc.FromStandardPath(bucketPath), // copy to same name
|
Name: o.fs.opt.Enc.FromStandardPath(bucketPath), // copy to same name
|
||||||
MetadataDirective: "REPLACE",
|
MetadataDirective: "REPLACE",
|
||||||
ContentType: info.ContentType,
|
ContentType: info.ContentType,
|
||||||
Info: info.Info,
|
Info: info.Info,
|
||||||
|
@ -1611,7 +1617,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read
|
||||||
opts.Path += "/b2api/v1/b2_download_file_by_id?fileId=" + urlEncode(o.id)
|
opts.Path += "/b2api/v1/b2_download_file_by_id?fileId=" + urlEncode(o.id)
|
||||||
} else {
|
} else {
|
||||||
bucket, bucketPath := o.split()
|
bucket, bucketPath := o.split()
|
||||||
opts.Path += "/file/" + urlEncode(enc.FromStandardName(bucket)) + "/" + urlEncode(enc.FromStandardPath(bucketPath))
|
opts.Path += "/file/" + urlEncode(o.fs.opt.Enc.FromStandardName(bucket)) + "/" + urlEncode(o.fs.opt.Enc.FromStandardPath(bucketPath))
|
||||||
}
|
}
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
|
@ -1808,7 +1814,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||||
Body: in,
|
Body: in,
|
||||||
ExtraHeaders: map[string]string{
|
ExtraHeaders: map[string]string{
|
||||||
"Authorization": upload.AuthorizationToken,
|
"Authorization": upload.AuthorizationToken,
|
||||||
"X-Bz-File-Name": urlEncode(enc.FromStandardPath(bucketPath)),
|
"X-Bz-File-Name": urlEncode(o.fs.opt.Enc.FromStandardPath(bucketPath)),
|
||||||
"Content-Type": fs.MimeType(ctx, src),
|
"Content-Type": fs.MimeType(ctx, src),
|
||||||
sha1Header: calculatedSha1,
|
sha1Header: calculatedSha1,
|
||||||
timeHeader: timeString(modTime),
|
timeHeader: timeString(modTime),
|
||||||
|
|
|
@ -111,7 +111,7 @@ func (f *Fs) newLargeUpload(ctx context.Context, o *Object, in io.Reader, src fs
|
||||||
}
|
}
|
||||||
var request = api.StartLargeFileRequest{
|
var request = api.StartLargeFileRequest{
|
||||||
BucketID: bucketID,
|
BucketID: bucketID,
|
||||||
Name: enc.FromStandardPath(bucketPath),
|
Name: f.opt.Enc.FromStandardPath(bucketPath),
|
||||||
ContentType: fs.MimeType(ctx, src),
|
ContentType: fs.MimeType(ctx, src),
|
||||||
Info: map[string]string{
|
Info: map[string]string{
|
||||||
timeKey: timeString(modTime),
|
timeKey: timeString(modTime),
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/jwtutil"
|
"github.com/rclone/rclone/lib/jwtutil"
|
||||||
|
|
||||||
"github.com/youmark/pkcs8"
|
"github.com/youmark/pkcs8"
|
||||||
|
@ -48,8 +49,6 @@ import (
|
||||||
"golang.org/x/oauth2/jws"
|
"golang.org/x/oauth2/jws"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.Box
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
rcloneClientID = "d0374ba6pgmaguie02ge15sv1mllndho"
|
rcloneClientID = "d0374ba6pgmaguie02ge15sv1mllndho"
|
||||||
rcloneEncryptedClientSecret = "sYbJYm99WB8jzeaLPU0OPDMJKIkZvD2qOn3SyEMfiJr03RdtDt3xcZEIudRhbIDL"
|
rcloneEncryptedClientSecret = "sYbJYm99WB8jzeaLPU0OPDMJKIkZvD2qOn3SyEMfiJr03RdtDt3xcZEIudRhbIDL"
|
||||||
|
@ -146,6 +145,11 @@ func init() {
|
||||||
Help: "Max number of times to try committing a multipart file.",
|
Help: "Max number of times to try committing a multipart file.",
|
||||||
Default: 100,
|
Default: 100,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Box,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -222,6 +226,7 @@ func getDecryptedPrivateKey(boxConfig *api.ConfigJSON) (key *rsa.PrivateKey, err
|
||||||
type Options struct {
|
type Options struct {
|
||||||
UploadCutoff fs.SizeSuffix `config:"upload_cutoff"`
|
UploadCutoff fs.SizeSuffix `config:"upload_cutoff"`
|
||||||
CommitRetries int `config:"commit_retries"`
|
CommitRetries int `config:"commit_retries"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote box
|
// Fs represents a remote box
|
||||||
|
@ -488,7 +493,7 @@ func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string,
|
||||||
Parameters: fieldsValue(),
|
Parameters: fieldsValue(),
|
||||||
}
|
}
|
||||||
mkdir := api.CreateFolder{
|
mkdir := api.CreateFolder{
|
||||||
Name: enc.FromStandardName(leaf),
|
Name: f.opt.Enc.FromStandardName(leaf),
|
||||||
Parent: api.Parent{
|
Parent: api.Parent{
|
||||||
ID: pathID,
|
ID: pathID,
|
||||||
},
|
},
|
||||||
|
@ -554,7 +559,7 @@ OUTER:
|
||||||
if item.ItemStatus != api.ItemStatusActive {
|
if item.ItemStatus != api.ItemStatusActive {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
item.Name = enc.ToStandardName(item.Name)
|
item.Name = f.opt.Enc.ToStandardName(item.Name)
|
||||||
if fn(item) {
|
if fn(item) {
|
||||||
found = true
|
found = true
|
||||||
break OUTER
|
break OUTER
|
||||||
|
@ -791,7 +796,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
Parameters: fieldsValue(),
|
Parameters: fieldsValue(),
|
||||||
}
|
}
|
||||||
copyFile := api.CopyFile{
|
copyFile := api.CopyFile{
|
||||||
Name: enc.FromStandardName(leaf),
|
Name: f.opt.Enc.FromStandardName(leaf),
|
||||||
Parent: api.Parent{
|
Parent: api.Parent{
|
||||||
ID: directoryID,
|
ID: directoryID,
|
||||||
},
|
},
|
||||||
|
@ -830,7 +835,7 @@ func (f *Fs) move(ctx context.Context, endpoint, id, leaf, directoryID string) (
|
||||||
Parameters: fieldsValue(),
|
Parameters: fieldsValue(),
|
||||||
}
|
}
|
||||||
move := api.UpdateFileMove{
|
move := api.UpdateFileMove{
|
||||||
Name: enc.FromStandardName(leaf),
|
Name: f.opt.Enc.FromStandardName(leaf),
|
||||||
Parent: api.Parent{
|
Parent: api.Parent{
|
||||||
ID: directoryID,
|
ID: directoryID,
|
||||||
},
|
},
|
||||||
|
@ -1155,7 +1160,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read
|
||||||
// This is recommended for less than 50 MB of content
|
// This is recommended for less than 50 MB of content
|
||||||
func (o *Object) upload(ctx context.Context, in io.Reader, leaf, directoryID string, modTime time.Time) (err error) {
|
func (o *Object) upload(ctx context.Context, in io.Reader, leaf, directoryID string, modTime time.Time) (err error) {
|
||||||
upload := api.UploadFile{
|
upload := api.UploadFile{
|
||||||
Name: enc.FromStandardName(leaf),
|
Name: o.fs.opt.Enc.FromStandardName(leaf),
|
||||||
ContentModifiedAt: api.Time(modTime),
|
ContentModifiedAt: api.Time(modTime),
|
||||||
ContentCreatedAt: api.Time(modTime),
|
ContentCreatedAt: api.Time(modTime),
|
||||||
Parent: api.Parent{
|
Parent: api.Parent{
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (o *Object) createUploadSession(ctx context.Context, leaf, directoryID stri
|
||||||
} else {
|
} else {
|
||||||
opts.Path = "/files/upload_sessions"
|
opts.Path = "/files/upload_sessions"
|
||||||
request.FolderID = directoryID
|
request.FolderID = directoryID
|
||||||
request.FileName = enc.FromStandardName(leaf)
|
request.FileName = o.fs.opt.Enc.FromStandardName(leaf)
|
||||||
}
|
}
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
|
|
|
@ -39,6 +39,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/fs/walk"
|
"github.com/rclone/rclone/fs/walk"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/readers"
|
"github.com/rclone/rclone/lib/readers"
|
||||||
|
@ -49,8 +50,6 @@ import (
|
||||||
"google.golang.org/api/googleapi"
|
"google.golang.org/api/googleapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.Drive
|
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const (
|
const (
|
||||||
rcloneClientID = "202264815644.apps.googleusercontent.com"
|
rcloneClientID = "202264815644.apps.googleusercontent.com"
|
||||||
|
@ -456,6 +455,11 @@ Google don't document so it may break in the future.
|
||||||
See: https://github.com/rclone/rclone/issues/3857
|
See: https://github.com/rclone/rclone/issues/3857
|
||||||
`,
|
`,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Drive,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -506,6 +510,7 @@ type Options struct {
|
||||||
ServerSideAcrossConfigs bool `config:"server_side_across_configs"`
|
ServerSideAcrossConfigs bool `config:"server_side_across_configs"`
|
||||||
DisableHTTP2 bool `config:"disable_http2"`
|
DisableHTTP2 bool `config:"disable_http2"`
|
||||||
StopOnUploadLimit bool `config:"stop_on_upload_limit"`
|
StopOnUploadLimit bool `config:"stop_on_upload_limit"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote drive server
|
// Fs represents a remote drive server
|
||||||
|
@ -677,7 +682,7 @@ func (f *Fs) list(ctx context.Context, dirIDs []string, title string, directorie
|
||||||
}
|
}
|
||||||
var stems []string
|
var stems []string
|
||||||
if title != "" {
|
if title != "" {
|
||||||
searchTitle := enc.FromStandardName(title)
|
searchTitle := f.opt.Enc.FromStandardName(title)
|
||||||
// Escaping the backslash isn't documented but seems to work
|
// Escaping the backslash isn't documented but seems to work
|
||||||
searchTitle = strings.Replace(searchTitle, `\`, `\\`, -1)
|
searchTitle = strings.Replace(searchTitle, `\`, `\\`, -1)
|
||||||
searchTitle = strings.Replace(searchTitle, `'`, `\'`, -1)
|
searchTitle = strings.Replace(searchTitle, `'`, `\'`, -1)
|
||||||
|
@ -751,7 +756,7 @@ OUTER:
|
||||||
return false, errors.Wrap(err, "couldn't list directory")
|
return false, errors.Wrap(err, "couldn't list directory")
|
||||||
}
|
}
|
||||||
for _, item := range files.Files {
|
for _, item := range files.Files {
|
||||||
item.Name = enc.ToStandardName(item.Name)
|
item.Name = f.opt.Enc.ToStandardName(item.Name)
|
||||||
// Check the case of items is correct since
|
// Check the case of items is correct since
|
||||||
// the `=` operator is case insensitive.
|
// the `=` operator is case insensitive.
|
||||||
if title != "" && title != item.Name {
|
if title != "" && title != item.Name {
|
||||||
|
@ -1313,7 +1318,7 @@ func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut strin
|
||||||
|
|
||||||
// CreateDir makes a directory with pathID as parent and name leaf
|
// CreateDir makes a directory with pathID as parent and name leaf
|
||||||
func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, err error) {
|
func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, err error) {
|
||||||
leaf = enc.FromStandardName(leaf)
|
leaf = f.opt.Enc.FromStandardName(leaf)
|
||||||
// fmt.Println("Making", path)
|
// fmt.Println("Making", path)
|
||||||
// Define the metadata for the directory we are going to create.
|
// Define the metadata for the directory we are going to create.
|
||||||
createInfo := &drive.File{
|
createInfo := &drive.File{
|
||||||
|
@ -1771,7 +1776,7 @@ func (f *Fs) createFileInfo(ctx context.Context, remote string, modTime time.Tim
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
leaf = enc.FromStandardName(leaf)
|
leaf = f.opt.Enc.FromStandardName(leaf)
|
||||||
// Define the metadata for the file we are going to create.
|
// Define the metadata for the file we are going to create.
|
||||||
createInfo := &drive.File{
|
createInfo := &drive.File{
|
||||||
Name: leaf,
|
Name: leaf,
|
||||||
|
@ -2470,7 +2475,7 @@ func (f *Fs) changeNotifyRunner(ctx context.Context, notifyFunc func(string, fs.
|
||||||
|
|
||||||
// find the new path
|
// find the new path
|
||||||
if change.File != nil {
|
if change.File != nil {
|
||||||
change.File.Name = enc.ToStandardName(change.File.Name)
|
change.File.Name = f.opt.Enc.ToStandardName(change.File.Name)
|
||||||
changeType := fs.EntryDirectory
|
changeType := fs.EntryDirectory
|
||||||
if change.File.MimeType != driveFolderType {
|
if change.File.MimeType != driveFolderType {
|
||||||
changeType = fs.EntryObject
|
changeType = fs.EntryObject
|
||||||
|
|
|
@ -48,14 +48,13 @@ import (
|
||||||
"github.com/rclone/rclone/fs/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
"github.com/rclone/rclone/fs/fserrors"
|
"github.com/rclone/rclone/fs/fserrors"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/readers"
|
"github.com/rclone/rclone/lib/readers"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.Dropbox
|
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const (
|
const (
|
||||||
rcloneClientID = "5jcck7diasz0rqy"
|
rcloneClientID = "5jcck7diasz0rqy"
|
||||||
|
@ -147,6 +146,11 @@ memory. It can be set smaller if you are tight on memory.`, maxChunkSize),
|
||||||
Help: "Impersonate this user when using a business account.",
|
Help: "Impersonate this user when using a business account.",
|
||||||
Default: "",
|
Default: "",
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Dropbox,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -155,6 +159,7 @@ memory. It can be set smaller if you are tight on memory.`, maxChunkSize),
|
||||||
type Options struct {
|
type Options struct {
|
||||||
ChunkSize fs.SizeSuffix `config:"chunk_size"`
|
ChunkSize fs.SizeSuffix `config:"chunk_size"`
|
||||||
Impersonate string `config:"impersonate"`
|
Impersonate string `config:"impersonate"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote dropbox server
|
// Fs represents a remote dropbox server
|
||||||
|
@ -381,7 +386,7 @@ func (f *Fs) setRoot(root string) {
|
||||||
func (f *Fs) getMetadata(objPath string) (entry files.IsMetadata, notFound bool, err error) {
|
func (f *Fs) getMetadata(objPath string) (entry files.IsMetadata, notFound bool, err error) {
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
entry, err = f.srv.GetMetadata(&files.GetMetadataArg{
|
entry, err = f.srv.GetMetadata(&files.GetMetadataArg{
|
||||||
Path: enc.FromStandardPath(objPath),
|
Path: f.opt.Enc.FromStandardPath(objPath),
|
||||||
})
|
})
|
||||||
return shouldRetry(err)
|
return shouldRetry(err)
|
||||||
})
|
})
|
||||||
|
@ -475,7 +480,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
for {
|
for {
|
||||||
if !started {
|
if !started {
|
||||||
arg := files.ListFolderArg{
|
arg := files.ListFolderArg{
|
||||||
Path: enc.FromStandardPath(root),
|
Path: f.opt.Enc.FromStandardPath(root),
|
||||||
Recursive: false,
|
Recursive: false,
|
||||||
}
|
}
|
||||||
if root == "/" {
|
if root == "/" {
|
||||||
|
@ -525,7 +530,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
|
|
||||||
// Only the last element is reliably cased in PathDisplay
|
// Only the last element is reliably cased in PathDisplay
|
||||||
entryPath := metadata.PathDisplay
|
entryPath := metadata.PathDisplay
|
||||||
leaf := enc.ToStandardName(path.Base(entryPath))
|
leaf := f.opt.Enc.ToStandardName(path.Base(entryPath))
|
||||||
remote := path.Join(dir, leaf)
|
remote := path.Join(dir, leaf)
|
||||||
if folderInfo != nil {
|
if folderInfo != nil {
|
||||||
d := fs.NewDir(remote, time.Now())
|
d := fs.NewDir(remote, time.Now())
|
||||||
|
@ -583,7 +588,7 @@ func (f *Fs) Mkdir(ctx context.Context, dir string) error {
|
||||||
|
|
||||||
// create it
|
// create it
|
||||||
arg2 := files.CreateFolderArg{
|
arg2 := files.CreateFolderArg{
|
||||||
Path: enc.FromStandardPath(root),
|
Path: f.opt.Enc.FromStandardPath(root),
|
||||||
}
|
}
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
_, err = f.srv.CreateFolderV2(&arg2)
|
_, err = f.srv.CreateFolderV2(&arg2)
|
||||||
|
@ -609,7 +614,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
|
||||||
return errors.Wrap(err, "Rmdir")
|
return errors.Wrap(err, "Rmdir")
|
||||||
}
|
}
|
||||||
|
|
||||||
root = enc.FromStandardPath(root)
|
root = f.opt.Enc.FromStandardPath(root)
|
||||||
// check directory empty
|
// check directory empty
|
||||||
arg := files.ListFolderArg{
|
arg := files.ListFolderArg{
|
||||||
Path: root,
|
Path: root,
|
||||||
|
@ -668,8 +673,8 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
// Copy
|
// Copy
|
||||||
arg := files.RelocationArg{
|
arg := files.RelocationArg{
|
||||||
RelocationPath: files.RelocationPath{
|
RelocationPath: files.RelocationPath{
|
||||||
FromPath: enc.FromStandardPath(srcObj.remotePath()),
|
FromPath: f.opt.Enc.FromStandardPath(srcObj.remotePath()),
|
||||||
ToPath: enc.FromStandardPath(dstObj.remotePath()),
|
ToPath: f.opt.Enc.FromStandardPath(dstObj.remotePath()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
|
@ -704,7 +709,7 @@ func (f *Fs) Purge(ctx context.Context) (err error) {
|
||||||
// Let dropbox delete the filesystem tree
|
// Let dropbox delete the filesystem tree
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
_, err = f.srv.DeleteV2(&files.DeleteArg{
|
_, err = f.srv.DeleteV2(&files.DeleteArg{
|
||||||
Path: enc.FromStandardPath(f.slashRoot),
|
Path: f.opt.Enc.FromStandardPath(f.slashRoot),
|
||||||
})
|
})
|
||||||
return shouldRetry(err)
|
return shouldRetry(err)
|
||||||
})
|
})
|
||||||
|
@ -736,8 +741,8 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
// Do the move
|
// Do the move
|
||||||
arg := files.RelocationArg{
|
arg := files.RelocationArg{
|
||||||
RelocationPath: files.RelocationPath{
|
RelocationPath: files.RelocationPath{
|
||||||
FromPath: enc.FromStandardPath(srcObj.remotePath()),
|
FromPath: f.opt.Enc.FromStandardPath(srcObj.remotePath()),
|
||||||
ToPath: enc.FromStandardPath(dstObj.remotePath()),
|
ToPath: f.opt.Enc.FromStandardPath(dstObj.remotePath()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
|
@ -764,7 +769,7 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
|
|
||||||
// PublicLink adds a "readable by anyone with link" permission on the given file or folder.
|
// PublicLink adds a "readable by anyone with link" permission on the given file or folder.
|
||||||
func (f *Fs) PublicLink(ctx context.Context, remote string) (link string, err error) {
|
func (f *Fs) PublicLink(ctx context.Context, remote string) (link string, err error) {
|
||||||
absPath := enc.FromStandardPath(path.Join(f.slashRoot, remote))
|
absPath := f.opt.Enc.FromStandardPath(path.Join(f.slashRoot, remote))
|
||||||
fs.Debugf(f, "attempting to share '%s' (absolute path: %s)", remote, absPath)
|
fs.Debugf(f, "attempting to share '%s' (absolute path: %s)", remote, absPath)
|
||||||
createArg := sharing.CreateSharedLinkWithSettingsArg{
|
createArg := sharing.CreateSharedLinkWithSettingsArg{
|
||||||
Path: absPath,
|
Path: absPath,
|
||||||
|
@ -840,8 +845,8 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string
|
||||||
// Do the move
|
// Do the move
|
||||||
arg := files.RelocationArg{
|
arg := files.RelocationArg{
|
||||||
RelocationPath: files.RelocationPath{
|
RelocationPath: files.RelocationPath{
|
||||||
FromPath: enc.FromStandardPath(srcPath),
|
FromPath: f.opt.Enc.FromStandardPath(srcPath),
|
||||||
ToPath: enc.FromStandardPath(dstPath),
|
ToPath: f.opt.Enc.FromStandardPath(dstPath),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
@ -999,7 +1004,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read
|
||||||
fs.FixRangeOption(options, o.bytes)
|
fs.FixRangeOption(options, o.bytes)
|
||||||
headers := fs.OpenOptionHeaders(options)
|
headers := fs.OpenOptionHeaders(options)
|
||||||
arg := files.DownloadArg{
|
arg := files.DownloadArg{
|
||||||
Path: enc.FromStandardPath(o.remotePath()),
|
Path: o.fs.opt.Enc.FromStandardPath(o.remotePath()),
|
||||||
ExtraHeaders: headers,
|
ExtraHeaders: headers,
|
||||||
}
|
}
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
|
@ -1130,7 +1135,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||||
if ignoredFiles.MatchString(remote) {
|
if ignoredFiles.MatchString(remote) {
|
||||||
return fserrors.NoRetryError(errors.Errorf("file name %q is disallowed - not uploading", path.Base(remote)))
|
return fserrors.NoRetryError(errors.Errorf("file name %q is disallowed - not uploading", path.Base(remote)))
|
||||||
}
|
}
|
||||||
commitInfo := files.NewCommitInfo(enc.FromStandardPath(o.remotePath()))
|
commitInfo := files.NewCommitInfo(o.fs.opt.Enc.FromStandardPath(o.remotePath()))
|
||||||
commitInfo.Mode.Tag = "overwrite"
|
commitInfo.Mode.Tag = "overwrite"
|
||||||
// The Dropbox API only accepts timestamps in UTC with second precision.
|
// The Dropbox API only accepts timestamps in UTC with second precision.
|
||||||
commitInfo.ClientModified = src.ModTime(ctx).UTC().Round(time.Second)
|
commitInfo.ClientModified = src.ModTime(ctx).UTC().Round(time.Second)
|
||||||
|
@ -1156,7 +1161,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||||
func (o *Object) Remove(ctx context.Context) (err error) {
|
func (o *Object) Remove(ctx context.Context) (err error) {
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
_, err = o.fs.srv.DeleteV2(&files.DeleteArg{
|
_, err = o.fs.srv.DeleteV2(&files.DeleteArg{
|
||||||
Path: enc.FromStandardPath(o.remotePath()),
|
Path: o.fs.opt.Enc.FromStandardPath(o.remotePath()),
|
||||||
})
|
})
|
||||||
return shouldRetry(err)
|
return shouldRetry(err)
|
||||||
})
|
})
|
||||||
|
|
|
@ -109,7 +109,7 @@ func (f *Fs) listFiles(ctx context.Context, directoryID int) (filesList *FilesLi
|
||||||
}
|
}
|
||||||
for i := range filesList.Items {
|
for i := range filesList.Items {
|
||||||
item := &filesList.Items[i]
|
item := &filesList.Items[i]
|
||||||
item.Filename = enc.ToStandardName(item.Filename)
|
item.Filename = f.opt.Enc.ToStandardName(item.Filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
return filesList, nil
|
return filesList, nil
|
||||||
|
@ -135,10 +135,10 @@ func (f *Fs) listFolders(ctx context.Context, directoryID int) (foldersList *Fol
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "couldn't list folders")
|
return nil, errors.Wrap(err, "couldn't list folders")
|
||||||
}
|
}
|
||||||
foldersList.Name = enc.ToStandardName(foldersList.Name)
|
foldersList.Name = f.opt.Enc.ToStandardName(foldersList.Name)
|
||||||
for i := range foldersList.SubFolders {
|
for i := range foldersList.SubFolders {
|
||||||
folder := &foldersList.SubFolders[i]
|
folder := &foldersList.SubFolders[i]
|
||||||
folder.Name = enc.ToStandardName(folder.Name)
|
folder.Name = f.opt.Enc.ToStandardName(folder.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fs.Debugf(f, "Got FoldersList for id `%s`", directoryID)
|
// fs.Debugf(f, "Got FoldersList for id `%s`", directoryID)
|
||||||
|
@ -213,7 +213,7 @@ func getRemote(dir, fileName string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fs) makeFolder(ctx context.Context, leaf string, folderID int) (response *MakeFolderResponse, err error) {
|
func (f *Fs) makeFolder(ctx context.Context, leaf string, folderID int) (response *MakeFolderResponse, err error) {
|
||||||
name := enc.FromStandardName(leaf)
|
name := f.opt.Enc.FromStandardName(leaf)
|
||||||
// fs.Debugf(f, "Creating folder `%s` in id `%s`", name, directoryID)
|
// fs.Debugf(f, "Creating folder `%s` in id `%s`", name, directoryID)
|
||||||
|
|
||||||
request := MakeFolderRequest{
|
request := MakeFolderRequest{
|
||||||
|
@ -323,7 +323,7 @@ func (f *Fs) getUploadNode(ctx context.Context) (response *GetUploadNodeResponse
|
||||||
func (f *Fs) uploadFile(ctx context.Context, in io.Reader, size int64, fileName, folderID, uploadID, node string) (response *http.Response, err error) {
|
func (f *Fs) uploadFile(ctx context.Context, in io.Reader, size int64, fileName, folderID, uploadID, node string) (response *http.Response, err error) {
|
||||||
// fs.Debugf(f, "Uploading File `%s`", fileName)
|
// fs.Debugf(f, "Uploading File `%s`", fileName)
|
||||||
|
|
||||||
fileName = enc.FromStandardName(fileName)
|
fileName = f.opt.Enc.FromStandardName(fileName)
|
||||||
|
|
||||||
if len(uploadID) > 10 || !isAlphaNumeric(uploadID) {
|
if len(uploadID) > 10 || !isAlphaNumeric(uploadID) {
|
||||||
return nil, errors.New("Invalid UploadID")
|
return nil, errors.New("Invalid UploadID")
|
||||||
|
|
|
@ -11,12 +11,14 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
"github.com/rclone/rclone/fs/fshttp"
|
"github.com/rclone/rclone/fs/fshttp"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
)
|
)
|
||||||
|
@ -29,8 +31,6 @@ const (
|
||||||
decayConstant = 2 // bigger for slower decay, exponential
|
decayConstant = 2 // bigger for slower decay, exponential
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.Fichier
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
fs.Register(&fs.RegInfo{
|
fs.Register(&fs.RegInfo{
|
||||||
Name: "fichier",
|
Name: "fichier",
|
||||||
|
@ -38,18 +38,20 @@ func init() {
|
||||||
Config: func(name string, config configmap.Mapper) {
|
Config: func(name string, config configmap.Mapper) {
|
||||||
},
|
},
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Options: []fs.Option{
|
Options: []fs.Option{{
|
||||||
{
|
|
||||||
Help: "Your API Key, get it from https://1fichier.com/console/params.pl",
|
Help: "Your API Key, get it from https://1fichier.com/console/params.pl",
|
||||||
Name: "api_key",
|
Name: "api_key",
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
Help: "If you want to download a shared folder, add this parameter",
|
Help: "If you want to download a shared folder, add this parameter",
|
||||||
Name: "shared_folder",
|
Name: "shared_folder",
|
||||||
Required: false,
|
Required: false,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
},
|
}, {
|
||||||
},
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Fichier,
|
||||||
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +59,7 @@ func init() {
|
||||||
type Options struct {
|
type Options struct {
|
||||||
APIKey string `config:"api_key"`
|
APIKey string `config:"api_key"`
|
||||||
SharedFolder string `config:"shared_folder"`
|
SharedFolder string `config:"shared_folder"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs is the interface a cloud storage system must provide
|
// Fs is the interface a cloud storage system must provide
|
||||||
|
@ -64,9 +67,9 @@ type Fs struct {
|
||||||
root string
|
root string
|
||||||
name string
|
name string
|
||||||
features *fs.Features
|
features *fs.Features
|
||||||
|
opt Options
|
||||||
dirCache *dircache.DirCache
|
dirCache *dircache.DirCache
|
||||||
baseClient *http.Client
|
baseClient *http.Client
|
||||||
options *Options
|
|
||||||
pacer *fs.Pacer
|
pacer *fs.Pacer
|
||||||
rest *rest.Client
|
rest *rest.Client
|
||||||
}
|
}
|
||||||
|
@ -162,7 +165,7 @@ func NewFs(name string, root string, config configmap.Mapper) (fs.Fs, error) {
|
||||||
f := &Fs{
|
f := &Fs{
|
||||||
name: name,
|
name: name,
|
||||||
root: root,
|
root: root,
|
||||||
options: opt,
|
opt: *opt,
|
||||||
pacer: fs.NewPacer(pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))),
|
pacer: fs.NewPacer(pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))),
|
||||||
baseClient: &http.Client{},
|
baseClient: &http.Client{},
|
||||||
}
|
}
|
||||||
|
@ -176,7 +179,7 @@ func NewFs(name string, root string, config configmap.Mapper) (fs.Fs, error) {
|
||||||
|
|
||||||
f.rest = rest.NewClient(client).SetRoot(apiBaseURL)
|
f.rest = rest.NewClient(client).SetRoot(apiBaseURL)
|
||||||
|
|
||||||
f.rest.SetHeader("Authorization", "Bearer "+f.options.APIKey)
|
f.rest.SetHeader("Authorization", "Bearer "+f.opt.APIKey)
|
||||||
|
|
||||||
f.dirCache = dircache.New(root, rootID, f)
|
f.dirCache = dircache.New(root, rootID, f)
|
||||||
|
|
||||||
|
@ -226,8 +229,8 @@ func NewFs(name string, root string, config configmap.Mapper) (fs.Fs, error) {
|
||||||
// This should return ErrDirNotFound if the directory isn't
|
// This should return ErrDirNotFound if the directory isn't
|
||||||
// found.
|
// found.
|
||||||
func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
|
func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
|
||||||
if f.options.SharedFolder != "" {
|
if f.opt.SharedFolder != "" {
|
||||||
return f.listSharedFiles(ctx, f.options.SharedFolder)
|
return f.listSharedFiles(ctx, f.opt.SharedFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
dirContent, err := f.listDir(ctx, dir)
|
dirContent, err := f.listDir(ctx, dir)
|
||||||
|
|
|
@ -14,25 +14,24 @@ import (
|
||||||
"github.com/jlaffaye/ftp"
|
"github.com/jlaffaye/ftp"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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/encodings"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"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{
|
||||||
Name: "ftp",
|
Name: "ftp",
|
||||||
Description: "FTP Connection",
|
Description: "FTP Connection",
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Options: []fs.Option{
|
Options: []fs.Option{{
|
||||||
{
|
|
||||||
Name: "host",
|
Name: "host",
|
||||||
Help: "FTP host to connect to",
|
Help: "FTP host to connect to",
|
||||||
Required: true,
|
Required: true,
|
||||||
|
@ -70,8 +69,12 @@ func init() {
|
||||||
Help: "Disable using EPSV even if server advertises support",
|
Help: "Disable using EPSV even if server advertises support",
|
||||||
Default: false,
|
Default: false,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
},
|
}, {
|
||||||
},
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.FTP,
|
||||||
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +88,7 @@ type Options struct {
|
||||||
Concurrency int `config:"concurrency"`
|
Concurrency int `config:"concurrency"`
|
||||||
SkipVerifyTLSCert bool `config:"no_check_certificate"`
|
SkipVerifyTLSCert bool `config:"no_check_certificate"`
|
||||||
DisableEPSV bool `config:"disable_epsv"`
|
DisableEPSV bool `config:"disable_epsv"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote FTP server
|
// Fs represents a remote FTP server
|
||||||
|
@ -308,22 +312,22 @@ func translateErrorDir(err error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// entryToStandard converts an incoming ftp.Entry to Standard encoding
|
// entryToStandard converts an incoming ftp.Entry to Standard encoding
|
||||||
func entryToStandard(entry *ftp.Entry) {
|
func (f *Fs) entryToStandard(entry *ftp.Entry) {
|
||||||
// Skip . and .. as we don't want these encoded
|
// Skip . and .. as we don't want these encoded
|
||||||
if entry.Name == "." || entry.Name == ".." {
|
if entry.Name == "." || entry.Name == ".." {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
entry.Name = enc.ToStandardName(entry.Name)
|
entry.Name = f.opt.Enc.ToStandardName(entry.Name)
|
||||||
entry.Target = enc.ToStandardPath(entry.Target)
|
entry.Target = f.opt.Enc.ToStandardPath(entry.Target)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dirFromStandardPath returns dir in encoded form.
|
// dirFromStandardPath returns dir in encoded form.
|
||||||
func dirFromStandardPath(dir string) string {
|
func (f *Fs) dirFromStandardPath(dir string) string {
|
||||||
// Skip . and .. as we don't want these encoded
|
// Skip . and .. as we don't want these encoded
|
||||||
if dir == "." || dir == ".." {
|
if dir == "." || dir == ".." {
|
||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
return enc.FromStandardPath(dir)
|
return f.opt.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
|
||||||
|
@ -345,13 +349,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(dirFromStandardPath(dir))
|
files, err := c.List(f.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)
|
f.entryToStandard(file)
|
||||||
if file.Name == base {
|
if file.Name == base {
|
||||||
return file, nil
|
return file, nil
|
||||||
}
|
}
|
||||||
|
@ -418,7 +422,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(dirFromStandardPath(path.Join(f.root, dir)))
|
result, err := c.List(f.dirFromStandardPath(path.Join(f.root, dir)))
|
||||||
f.putFtpConnection(&c, err)
|
f.putFtpConnection(&c, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errchan <- err
|
errchan <- err
|
||||||
|
@ -455,7 +459,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)
|
f.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:
|
||||||
|
@ -525,7 +529,7 @@ 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(dirFromStandardPath(dir))
|
files, err := c.List(f.dirFromStandardPath(dir))
|
||||||
f.putFtpConnection(&c, err)
|
f.putFtpConnection(&c, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, translateErrorFile(err)
|
return nil, translateErrorFile(err)
|
||||||
|
@ -533,7 +537,7 @@ func (f *Fs) getInfo(remote string) (fi *FileInfo, err error) {
|
||||||
|
|
||||||
for i := range files {
|
for i := range files {
|
||||||
file := files[i]
|
file := files[i]
|
||||||
entryToStandard(file)
|
f.entryToStandard(file)
|
||||||
if file.Name == base {
|
if file.Name == base {
|
||||||
info := &FileInfo{
|
info := &FileInfo{
|
||||||
Name: remote,
|
Name: remote,
|
||||||
|
@ -571,7 +575,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(dirFromStandardPath(abspath))
|
err = c.MakeDir(f.dirFromStandardPath(abspath))
|
||||||
f.putFtpConnection(&c, err)
|
f.putFtpConnection(&c, err)
|
||||||
switch errX := err.(type) {
|
switch errX := err.(type) {
|
||||||
case *textproto.Error:
|
case *textproto.Error:
|
||||||
|
@ -607,7 +611,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(dirFromStandardPath(path.Join(f.root, dir)))
|
err = c.RemoveDir(f.dirFromStandardPath(path.Join(f.root, dir)))
|
||||||
f.putFtpConnection(&c, err)
|
f.putFtpConnection(&c, err)
|
||||||
return translateErrorDir(err)
|
return translateErrorDir(err)
|
||||||
}
|
}
|
||||||
|
@ -628,8 +632,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(
|
||||||
enc.FromStandardPath(path.Join(srcObj.fs.root, srcObj.remote)),
|
f.opt.Enc.FromStandardPath(path.Join(srcObj.fs.root, srcObj.remote)),
|
||||||
enc.FromStandardPath(path.Join(f.root, remote)),
|
f.opt.Enc.FromStandardPath(path.Join(f.root, remote)),
|
||||||
)
|
)
|
||||||
f.putFtpConnection(&c, err)
|
f.putFtpConnection(&c, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -682,8 +686,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(
|
||||||
dirFromStandardPath(srcPath),
|
f.dirFromStandardPath(srcPath),
|
||||||
dirFromStandardPath(dstPath),
|
f.dirFromStandardPath(dstPath),
|
||||||
)
|
)
|
||||||
f.putFtpConnection(&c, err)
|
f.putFtpConnection(&c, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -809,7 +813,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(enc.FromStandardPath(path), uint64(offset))
|
fd, err := c.RetrFrom(o.fs.opt.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")
|
||||||
|
@ -844,7 +848,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(enc.FromStandardPath(path), in)
|
err = c.Stor(o.fs.opt.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()
|
||||||
|
@ -874,7 +878,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(enc.FromStandardPath(path))
|
err = c.Delete(o.fs.opt.Enc.FromStandardPath(path))
|
||||||
o.fs.putFtpConnection(&c, err)
|
o.fs.putFtpConnection(&c, err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/fs/walk"
|
"github.com/rclone/rclone/fs/walk"
|
||||||
"github.com/rclone/rclone/lib/bucket"
|
"github.com/rclone/rclone/lib/bucket"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
@ -69,8 +70,6 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.GoogleCloudStorage
|
|
||||||
|
|
||||||
// Register with Fs
|
// Register with Fs
|
||||||
func init() {
|
func init() {
|
||||||
fs.Register(&fs.RegInfo{
|
fs.Register(&fs.RegInfo{
|
||||||
|
@ -248,6 +247,11 @@ Docs: https://cloud.google.com/storage/docs/bucket-policy-only
|
||||||
Value: "DURABLE_REDUCED_AVAILABILITY",
|
Value: "DURABLE_REDUCED_AVAILABILITY",
|
||||||
Help: "Durable reduced availability storage class",
|
Help: "Durable reduced availability storage class",
|
||||||
}},
|
}},
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.GoogleCloudStorage,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -262,6 +266,7 @@ type Options struct {
|
||||||
BucketPolicyOnly bool `config:"bucket_policy_only"`
|
BucketPolicyOnly bool `config:"bucket_policy_only"`
|
||||||
Location string `config:"location"`
|
Location string `config:"location"`
|
||||||
StorageClass string `config:"storage_class"`
|
StorageClass string `config:"storage_class"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote storage server
|
// Fs represents a remote storage server
|
||||||
|
@ -353,7 +358,7 @@ func parsePath(path string) (root string) {
|
||||||
// relative to f.root
|
// relative to f.root
|
||||||
func (f *Fs) split(rootRelativePath string) (bucketName, bucketPath string) {
|
func (f *Fs) split(rootRelativePath string) (bucketName, bucketPath string) {
|
||||||
bucketName, bucketPath = bucket.Split(path.Join(f.root, rootRelativePath))
|
bucketName, bucketPath = bucket.Split(path.Join(f.root, rootRelativePath))
|
||||||
return enc.FromStandardName(bucketName), enc.FromStandardPath(bucketPath)
|
return f.opt.Enc.FromStandardName(bucketName), f.opt.Enc.FromStandardPath(bucketPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// split returns bucket and bucketPath from the object
|
// split returns bucket and bucketPath from the object
|
||||||
|
@ -442,7 +447,7 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
|
||||||
|
|
||||||
if f.rootBucket != "" && f.rootDirectory != "" {
|
if f.rootBucket != "" && f.rootDirectory != "" {
|
||||||
// Check to see if the object exists
|
// Check to see if the object exists
|
||||||
encodedDirectory := enc.FromStandardPath(f.rootDirectory)
|
encodedDirectory := f.opt.Enc.FromStandardPath(f.rootDirectory)
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
_, err = f.svc.Objects.Get(f.rootBucket, encodedDirectory).Context(ctx).Do()
|
_, err = f.svc.Objects.Get(f.rootBucket, encodedDirectory).Context(ctx).Do()
|
||||||
return shouldRetry(err)
|
return shouldRetry(err)
|
||||||
|
@ -527,7 +532,7 @@ func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBuck
|
||||||
if !strings.HasSuffix(remote, "/") {
|
if !strings.HasSuffix(remote, "/") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
remote = enc.ToStandardPath(remote)
|
remote = f.opt.Enc.ToStandardPath(remote)
|
||||||
if !strings.HasPrefix(remote, prefix) {
|
if !strings.HasPrefix(remote, prefix) {
|
||||||
fs.Logf(f, "Odd name received %q", remote)
|
fs.Logf(f, "Odd name received %q", remote)
|
||||||
continue
|
continue
|
||||||
|
@ -543,7 +548,7 @@ func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBuck
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, object := range objects.Items {
|
for _, object := range objects.Items {
|
||||||
remote := enc.ToStandardPath(object.Name)
|
remote := f.opt.Enc.ToStandardPath(object.Name)
|
||||||
if !strings.HasPrefix(remote, prefix) {
|
if !strings.HasPrefix(remote, prefix) {
|
||||||
fs.Logf(f, "Odd name received %q", object.Name)
|
fs.Logf(f, "Odd name received %q", object.Name)
|
||||||
continue
|
continue
|
||||||
|
@ -620,7 +625,7 @@ func (f *Fs) listBuckets(ctx context.Context) (entries fs.DirEntries, err error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, bucket := range buckets.Items {
|
for _, bucket := range buckets.Items {
|
||||||
d := fs.NewDir(enc.ToStandardName(bucket.Name), time.Time{})
|
d := fs.NewDir(f.opt.Enc.ToStandardName(bucket.Name), time.Time{})
|
||||||
entries = append(entries, d)
|
entries = append(entries, d)
|
||||||
}
|
}
|
||||||
if buckets.NextPageToken == "" {
|
if buckets.NextPageToken == "" {
|
||||||
|
|
|
@ -31,14 +31,13 @@ import (
|
||||||
"github.com/rclone/rclone/fs/fshttp"
|
"github.com/rclone/rclone/fs/fshttp"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/fs/walk"
|
"github.com/rclone/rclone/fs/walk"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.JottaCloud
|
|
||||||
|
|
||||||
// Globals
|
// Globals
|
||||||
const (
|
const (
|
||||||
minSleep = 10 * time.Millisecond
|
minSleep = 10 * time.Millisecond
|
||||||
|
@ -157,6 +156,11 @@ func init() {
|
||||||
Help: "Files bigger than this can be resumed if the upload fail's.",
|
Help: "Files bigger than this can be resumed if the upload fail's.",
|
||||||
Default: fs.SizeSuffix(10 * 1024 * 1024),
|
Default: fs.SizeSuffix(10 * 1024 * 1024),
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.JottaCloud,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -169,6 +173,7 @@ type Options struct {
|
||||||
HardDelete bool `config:"hard_delete"`
|
HardDelete bool `config:"hard_delete"`
|
||||||
Unlink bool `config:"unlink"`
|
Unlink bool `config:"unlink"`
|
||||||
UploadThreshold fs.SizeSuffix `config:"upload_resume_limit"`
|
UploadThreshold fs.SizeSuffix `config:"upload_resume_limit"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote jottacloud
|
// Fs represents a remote jottacloud
|
||||||
|
@ -446,7 +451,7 @@ func urlPathEscape(in string) string {
|
||||||
|
|
||||||
// filePathRaw returns an unescaped file path (f.root, file)
|
// filePathRaw returns an unescaped file path (f.root, file)
|
||||||
func (f *Fs) filePathRaw(file string) string {
|
func (f *Fs) filePathRaw(file string) string {
|
||||||
return path.Join(f.endpointURL, enc.FromStandardPath(path.Join(f.root, file)))
|
return path.Join(f.endpointURL, f.opt.Enc.FromStandardPath(path.Join(f.root, file)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// filePath returns a escaped file path (f.root, file)
|
// filePath returns a escaped file path (f.root, file)
|
||||||
|
@ -638,7 +643,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
if item.Deleted {
|
if item.Deleted {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
remote := path.Join(dir, enc.ToStandardName(item.Name))
|
remote := path.Join(dir, f.opt.Enc.ToStandardName(item.Name))
|
||||||
d := fs.NewDir(remote, time.Time(item.ModifiedAt))
|
d := fs.NewDir(remote, time.Time(item.ModifiedAt))
|
||||||
entries = append(entries, d)
|
entries = append(entries, d)
|
||||||
}
|
}
|
||||||
|
@ -648,7 +653,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
if item.Deleted || item.State != "COMPLETED" {
|
if item.Deleted || item.State != "COMPLETED" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
remote := path.Join(dir, enc.ToStandardName(item.Name))
|
remote := path.Join(dir, f.opt.Enc.ToStandardName(item.Name))
|
||||||
o, err := f.newObjectWithInfo(ctx, remote, item)
|
o, err := f.newObjectWithInfo(ctx, remote, item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
@ -673,7 +678,7 @@ func (f *Fs) listFileDir(ctx context.Context, remoteStartPath string, startFolde
|
||||||
if folder.Deleted {
|
if folder.Deleted {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
folderPath := enc.ToStandardPath(path.Join(folder.Path, folder.Name))
|
folderPath := f.opt.Enc.ToStandardPath(path.Join(folder.Path, folder.Name))
|
||||||
folderPathLength := len(folderPath)
|
folderPathLength := len(folderPath)
|
||||||
var remoteDir string
|
var remoteDir string
|
||||||
if folderPathLength > pathPrefixLength {
|
if folderPathLength > pathPrefixLength {
|
||||||
|
@ -691,7 +696,7 @@ func (f *Fs) listFileDir(ctx context.Context, remoteStartPath string, startFolde
|
||||||
if file.Deleted || file.State != "COMPLETED" {
|
if file.Deleted || file.State != "COMPLETED" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
remoteFile := path.Join(remoteDir, enc.ToStandardName(file.Name))
|
remoteFile := path.Join(remoteDir, f.opt.Enc.ToStandardName(file.Name))
|
||||||
o, err := f.newObjectWithInfo(ctx, remoteFile, file)
|
o, err := f.newObjectWithInfo(ctx, remoteFile, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -862,7 +867,7 @@ func (f *Fs) copyOrMove(ctx context.Context, method, src, dest string) (info *ap
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Parameters.Set(method, "/"+path.Join(f.endpointURL, enc.FromStandardPath(path.Join(f.root, dest))))
|
opts.Parameters.Set(method, "/"+path.Join(f.endpointURL, f.opt.Enc.FromStandardPath(path.Join(f.root, dest))))
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
@ -969,7 +974,7 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string
|
||||||
return fs.ErrorDirExists
|
return fs.ErrorDirExists
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = f.copyOrMove(ctx, "mvDir", path.Join(f.endpointURL, enc.FromStandardPath(srcPath))+"/", dstRemote)
|
_, err = f.copyOrMove(ctx, "mvDir", path.Join(f.endpointURL, f.opt.Enc.FromStandardPath(srcPath))+"/", dstRemote)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "couldn't move directory")
|
return errors.Wrap(err, "couldn't move directory")
|
||||||
|
@ -1260,7 +1265,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||||
Created: fileDate,
|
Created: fileDate,
|
||||||
Modified: fileDate,
|
Modified: fileDate,
|
||||||
Md5: md5String,
|
Md5: md5String,
|
||||||
Path: path.Join(o.fs.opt.Mountpoint, enc.FromStandardPath(path.Join(o.fs.root, o.remote))),
|
Path: path.Join(o.fs.opt.Mountpoint, o.fs.opt.Enc.FromStandardPath(path.Join(o.fs.root, o.remote))),
|
||||||
}
|
}
|
||||||
|
|
||||||
// send it
|
// send it
|
||||||
|
|
|
@ -12,27 +12,26 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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/encodings"
|
||||||
"github.com/rclone/rclone/fs/fshttp"
|
"github.com/rclone/rclone/fs/fshttp"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
|
|
||||||
httpclient "github.com/koofr/go-httpclient"
|
httpclient "github.com/koofr/go-httpclient"
|
||||||
koofrclient "github.com/koofr/go-koofrclient"
|
koofrclient "github.com/koofr/go-koofrclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.Koofr
|
|
||||||
|
|
||||||
// Register Fs with rclone
|
// Register Fs with rclone
|
||||||
func init() {
|
func init() {
|
||||||
fs.Register(&fs.RegInfo{
|
fs.Register(&fs.RegInfo{
|
||||||
Name: "koofr",
|
Name: "koofr",
|
||||||
Description: "Koofr",
|
Description: "Koofr",
|
||||||
NewFs: NewFs,
|
NewFs: NewFs,
|
||||||
Options: []fs.Option{
|
Options: []fs.Option{{
|
||||||
{
|
|
||||||
Name: "endpoint",
|
Name: "endpoint",
|
||||||
Help: "The Koofr API endpoint to use",
|
Help: "The Koofr API endpoint to use",
|
||||||
Default: "https://app.koofr.net",
|
Default: "https://app.koofr.net",
|
||||||
|
@ -59,8 +58,12 @@ func init() {
|
||||||
Help: "Your Koofr password for rclone (generate one at https://app.koofr.net/app/admin/preferences/password)",
|
Help: "Your Koofr password for rclone (generate one at https://app.koofr.net/app/admin/preferences/password)",
|
||||||
IsPassword: true,
|
IsPassword: true,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
}, {
|
||||||
},
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Koofr,
|
||||||
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +74,7 @@ type Options struct {
|
||||||
User string `config:"user"`
|
User string `config:"user"`
|
||||||
Password string `config:"password"`
|
Password string `config:"password"`
|
||||||
SetMTime bool `config:"setmtime"`
|
SetMTime bool `config:"setmtime"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Fs is a representation of a remote Koofr Fs
|
// A Fs is a representation of a remote Koofr Fs
|
||||||
|
@ -246,7 +250,7 @@ func (f *Fs) Hashes() hash.Set {
|
||||||
|
|
||||||
// fullPath constructs a full, absolute path from a Fs root relative path,
|
// fullPath constructs a full, absolute path from a Fs root relative path,
|
||||||
func (f *Fs) fullPath(part string) string {
|
func (f *Fs) fullPath(part string) string {
|
||||||
return enc.FromStandardPath(path.Join("/", f.root, part))
|
return f.opt.Enc.FromStandardPath(path.Join("/", f.root, part))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFs constructs a new filesystem given a root path and configuration options
|
// NewFs constructs a new filesystem given a root path and configuration options
|
||||||
|
@ -299,7 +303,7 @@ func NewFs(name, root string, m configmap.Mapper) (ff fs.Fs, err error) {
|
||||||
}
|
}
|
||||||
return nil, errors.New("Failed to find mount " + opt.MountID)
|
return nil, errors.New("Failed to find mount " + opt.MountID)
|
||||||
}
|
}
|
||||||
rootFile, err := f.client.FilesInfo(f.mountID, enc.FromStandardPath("/"+f.root))
|
rootFile, err := f.client.FilesInfo(f.mountID, f.opt.Enc.FromStandardPath("/"+f.root))
|
||||||
if err == nil && rootFile.Type != "dir" {
|
if err == nil && rootFile.Type != "dir" {
|
||||||
f.root = dir(f.root)
|
f.root = dir(f.root)
|
||||||
err = fs.ErrorIsFile
|
err = fs.ErrorIsFile
|
||||||
|
@ -317,7 +321,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
}
|
}
|
||||||
entries = make([]fs.DirEntry, len(files))
|
entries = make([]fs.DirEntry, len(files))
|
||||||
for i, file := range files {
|
for i, file := range files {
|
||||||
remote := path.Join(dir, enc.ToStandardName(file.Name))
|
remote := path.Join(dir, f.opt.Enc.ToStandardName(file.Name))
|
||||||
if file.Type == "dir" {
|
if file.Type == "dir" {
|
||||||
entries[i] = fs.NewDir(remote, time.Unix(0, 0))
|
entries[i] = fs.NewDir(remote, time.Unix(0, 0))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,4 +6,4 @@ import (
|
||||||
"github.com/rclone/rclone/fs/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.LocalMacOS
|
const defaultEnc = encodings.LocalMacOS
|
||||||
|
|
|
@ -6,4 +6,4 @@ import (
|
||||||
"github.com/rclone/rclone/fs/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.LocalUnix
|
const defaultEnc = encodings.LocalUnix
|
||||||
|
|
|
@ -6,4 +6,4 @@ import (
|
||||||
"github.com/rclone/rclone/fs/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.LocalWindows
|
const defaultEnc = encodings.LocalWindows
|
||||||
|
|
|
@ -20,10 +20,12 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
"github.com/rclone/rclone/fs/accounting"
|
"github.com/rclone/rclone/fs/accounting"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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/fserrors"
|
"github.com/rclone/rclone/fs/fserrors"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/file"
|
"github.com/rclone/rclone/lib/file"
|
||||||
"github.com/rclone/rclone/lib/readers"
|
"github.com/rclone/rclone/lib/readers"
|
||||||
)
|
)
|
||||||
|
@ -115,6 +117,11 @@ Windows/macOS and case sensitive for everything else. Use this flag
|
||||||
to override the default choice.`,
|
to override the default choice.`,
|
||||||
Default: false,
|
Default: false,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: defaultEnc,
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
fs.Register(fsi)
|
fs.Register(fsi)
|
||||||
|
@ -131,6 +138,7 @@ type Options struct {
|
||||||
OneFileSystem bool `config:"one_file_system"`
|
OneFileSystem bool `config:"one_file_system"`
|
||||||
CaseSensitive bool `config:"case_sensitive"`
|
CaseSensitive bool `config:"case_sensitive"`
|
||||||
CaseInsensitive bool `config:"case_insensitive"`
|
CaseInsensitive bool `config:"case_insensitive"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a local filesystem rooted at root
|
// Fs represents a local filesystem rooted at root
|
||||||
|
@ -189,7 +197,7 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
|
||||||
dev: devUnset,
|
dev: devUnset,
|
||||||
lstat: os.Lstat,
|
lstat: os.Lstat,
|
||||||
}
|
}
|
||||||
f.root = cleanRootPath(root, f.opt.NoUNC)
|
f.root = cleanRootPath(root, f.opt.NoUNC, f.opt.Enc)
|
||||||
f.features = (&fs.Features{
|
f.features = (&fs.Features{
|
||||||
CaseInsensitive: f.caseInsensitive(),
|
CaseInsensitive: f.caseInsensitive(),
|
||||||
CanHaveEmptyDirectories: true,
|
CanHaveEmptyDirectories: true,
|
||||||
|
@ -234,7 +242,7 @@ func (f *Fs) Name() string {
|
||||||
|
|
||||||
// Root of the remote (as passed into NewFs)
|
// Root of the remote (as passed into NewFs)
|
||||||
func (f *Fs) Root() string {
|
func (f *Fs) Root() string {
|
||||||
return enc.ToStandardPath(filepath.ToSlash(f.root))
|
return f.opt.Enc.ToStandardPath(filepath.ToSlash(f.root))
|
||||||
}
|
}
|
||||||
|
|
||||||
// String converts this Fs to a string
|
// String converts this Fs to a string
|
||||||
|
@ -443,7 +451,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fs) cleanRemote(dir, filename string) (remote string) {
|
func (f *Fs) cleanRemote(dir, filename string) (remote string) {
|
||||||
remote = path.Join(dir, enc.ToStandardName(filename))
|
remote = path.Join(dir, f.opt.Enc.ToStandardName(filename))
|
||||||
|
|
||||||
if !utf8.ValidString(filename) {
|
if !utf8.ValidString(filename) {
|
||||||
f.warnedMu.Lock()
|
f.warnedMu.Lock()
|
||||||
|
@ -457,7 +465,7 @@ func (f *Fs) cleanRemote(dir, filename string) (remote string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fs) localPath(name string) string {
|
func (f *Fs) localPath(name string) string {
|
||||||
return filepath.Join(f.root, filepath.FromSlash(enc.FromStandardPath(name)))
|
return filepath.Join(f.root, filepath.FromSlash(f.opt.Enc.FromStandardPath(name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put the Object to the local filesystem
|
// Put the Object to the local filesystem
|
||||||
|
@ -1092,7 +1100,7 @@ func (o *Object) Remove(ctx context.Context) error {
|
||||||
return remove(o.path)
|
return remove(o.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanRootPath(s string, noUNC bool) string {
|
func cleanRootPath(s string, noUNC bool, enc encoder.MultiEncoder) string {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
if !filepath.IsAbs(s) && !strings.HasPrefix(s, "\\") {
|
if !filepath.IsAbs(s) && !strings.HasPrefix(s, "\\") {
|
||||||
s2, err := filepath.Abs(s)
|
s2, err := filepath.Abs(s)
|
||||||
|
|
|
@ -64,7 +64,7 @@ func TestCleanWindows(t *testing.T) {
|
||||||
t.Skipf("windows only")
|
t.Skipf("windows only")
|
||||||
}
|
}
|
||||||
for _, test := range testsWindows {
|
for _, test := range testsWindows {
|
||||||
got := cleanRootPath(test[0], true)
|
got := cleanRootPath(test[0], true, defaultEnc)
|
||||||
expect := test[1]
|
expect := test[1]
|
||||||
if got != expect {
|
if got != expect {
|
||||||
t.Fatalf("got %q, expected %q", got, expect)
|
t.Fatalf("got %q, expected %q", got, expect)
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/rclone/rclone/backend/mailru/mrhash"
|
"github.com/rclone/rclone/backend/mailru/mrhash"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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"
|
||||||
|
@ -34,6 +35,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/object"
|
"github.com/rclone/rclone/fs/object"
|
||||||
"github.com/rclone/rclone/fs/operations"
|
"github.com/rclone/rclone/fs/operations"
|
||||||
|
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
|
@ -42,8 +44,6 @@ 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
|
||||||
|
@ -193,6 +193,11 @@ facilitate remote troubleshooting of backend issues. Strict meaning of
|
||||||
flags is not documented and not guaranteed to persist between releases.
|
flags is not documented and not guaranteed to persist between releases.
|
||||||
Quirks will be removed when the backend grows stable.
|
Quirks will be removed when the backend grows stable.
|
||||||
Supported quirks: atomicmkdir binlist gzip insecure retry400`,
|
Supported quirks: atomicmkdir binlist gzip insecure retry400`,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Mailru,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -208,6 +213,7 @@ type Options struct {
|
||||||
SpeedupMaxDisk fs.SizeSuffix `config:"speedup_max_disk"`
|
SpeedupMaxDisk fs.SizeSuffix `config:"speedup_max_disk"`
|
||||||
SpeedupMaxMem fs.SizeSuffix `config:"speedup_max_memory"`
|
SpeedupMaxMem fs.SizeSuffix `config:"speedup_max_memory"`
|
||||||
Quirks string `config:"quirks"`
|
Quirks string `config:"quirks"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// retryErrorCodes is a slice of error codes that we will retry
|
// retryErrorCodes is a slice of error codes that we will retry
|
||||||
|
@ -607,7 +613,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": {enc.FromStandardPath(path)},
|
"home": {f.opt.Enc.FromStandardPath(path)},
|
||||||
"offset": {"0"},
|
"offset": {"0"},
|
||||||
"limit": {strconv.Itoa(maxInt32)},
|
"limit": {strconv.Itoa(maxInt32)},
|
||||||
},
|
},
|
||||||
|
@ -642,7 +648,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(enc.ToStandardPath(item.Home))
|
remote, err := f.relPath(f.opt.Enc.ToStandardPath(item.Home))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
|
@ -708,7 +714,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", enc.FromStandardPath(dirPath))
|
data.Set("home", f.opt.Enc.FromStandardPath(dirPath))
|
||||||
|
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
|
@ -756,7 +762,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(enc.FromStandardPath(dirPath))
|
req.WriteString(f.opt.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)
|
||||||
|
@ -892,7 +898,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 := enc.FromStandardPath(string(r.ReadBytesByLength()))
|
name := t.f.opt.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
|
||||||
|
@ -1031,7 +1037,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(enc.FromStandardPath(path))
|
req.WriteString(f.opt.Enc.FromStandardPath(path))
|
||||||
req.WritePu32(0)
|
req.WritePu32(0)
|
||||||
|
|
||||||
token, err := f.accessToken()
|
token, err := f.accessToken()
|
||||||
|
@ -1186,7 +1192,7 @@ func (f *Fs) delete(ctx context.Context, path string, hardDelete bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{"home": {enc.FromStandardPath(path)}}
|
data := url.Values{"home": {f.opt.Enc.FromStandardPath(path)}}
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
Path: "/api/m1/file/remove",
|
Path: "/api/m1/file/remove",
|
||||||
|
@ -1243,8 +1249,8 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
data := url.Values{}
|
||||||
data.Set("home", enc.FromStandardPath(srcPath))
|
data.Set("home", f.opt.Enc.FromStandardPath(srcPath))
|
||||||
data.Set("folder", enc.FromStandardPath(parentDir(dstPath)))
|
data.Set("folder", f.opt.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)
|
||||||
|
|
||||||
|
@ -1282,7 +1288,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 := enc.ToStandardPath(response.Body)
|
tmpPath := f.opt.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")
|
||||||
|
@ -1357,9 +1363,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(enc.FromStandardPath(srcPath))
|
req.WriteString(f.opt.Enc.FromStandardPath(srcPath))
|
||||||
req.WritePu32(0) // new revision
|
req.WritePu32(0) // new revision
|
||||||
req.WriteString(enc.FromStandardPath(dstPath))
|
req.WriteString(f.opt.Enc.FromStandardPath(dstPath))
|
||||||
req.WritePu32(0) // dunno
|
req.WritePu32(0) // dunno
|
||||||
|
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
|
@ -1450,7 +1456,7 @@ func (f *Fs) PublicLink(ctx context.Context, remote string) (link string, err er
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
data := url.Values{}
|
||||||
data.Set("home", enc.FromStandardPath(f.absPath(remote)))
|
data.Set("home", f.opt.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)
|
||||||
|
|
||||||
|
@ -2015,7 +2021,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(enc.FromStandardPath(o.absPath()))
|
req.WriteString(o.fs.opt.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)
|
||||||
|
@ -2113,7 +2119,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(enc.FromStandardPath(o.absPath()), "/")),
|
Path: url.PathEscape(strings.TrimLeft(o.fs.opt.Enc.FromStandardPath(o.absPath()), "/")),
|
||||||
Parameters: url.Values{
|
Parameters: url.Values{
|
||||||
"client_id": {api.OAuthClientID},
|
"client_id": {api.OAuthClientID},
|
||||||
"token": {token},
|
"token": {token},
|
||||||
|
|
|
@ -26,19 +26,19 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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/encodings"
|
||||||
"github.com/rclone/rclone/fs/fshttp"
|
"github.com/rclone/rclone/fs/fshttp"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/readers"
|
"github.com/rclone/rclone/lib/readers"
|
||||||
mega "github.com/t3rm1n4l/go-mega"
|
mega "github.com/t3rm1n4l/go-mega"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.Mega
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minSleep = 10 * time.Millisecond
|
minSleep = 10 * time.Millisecond
|
||||||
maxSleep = 2 * time.Second
|
maxSleep = 2 * time.Second
|
||||||
|
@ -83,6 +83,11 @@ than permanently deleting them. If you specify this then rclone will
|
||||||
permanently delete objects instead.`,
|
permanently delete objects instead.`,
|
||||||
Default: false,
|
Default: false,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Mega,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -93,6 +98,7 @@ type Options struct {
|
||||||
Pass string `config:"pass"`
|
Pass string `config:"pass"`
|
||||||
Debug bool `config:"debug"`
|
Debug bool `config:"debug"`
|
||||||
HardDelete bool `config:"hard_delete"`
|
HardDelete bool `config:"hard_delete"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote mega
|
// Fs represents a remote mega
|
||||||
|
@ -250,12 +256,12 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
|
||||||
// splitNodePath splits nodePath into / separated parts, returning nil if it
|
// splitNodePath splits nodePath into / separated parts, returning nil if it
|
||||||
// should refer to the root.
|
// should refer to the root.
|
||||||
// It also encodes the parts into backend specific encoding
|
// It also encodes the parts into backend specific encoding
|
||||||
func splitNodePath(nodePath string) (parts []string) {
|
func (f *Fs) splitNodePath(nodePath string) (parts []string) {
|
||||||
nodePath = path.Clean(nodePath)
|
nodePath = path.Clean(nodePath)
|
||||||
if nodePath == "." || nodePath == "/" {
|
if nodePath == "." || nodePath == "/" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
nodePath = enc.FromStandardPath(nodePath)
|
nodePath = f.opt.Enc.FromStandardPath(nodePath)
|
||||||
return strings.Split(nodePath, "/")
|
return strings.Split(nodePath, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +269,7 @@ func splitNodePath(nodePath string) (parts []string) {
|
||||||
//
|
//
|
||||||
// It returns mega.ENOENT if it wasn't found
|
// It returns mega.ENOENT if it wasn't found
|
||||||
func (f *Fs) findNode(rootNode *mega.Node, nodePath string) (*mega.Node, error) {
|
func (f *Fs) findNode(rootNode *mega.Node, nodePath string) (*mega.Node, error) {
|
||||||
parts := splitNodePath(nodePath)
|
parts := f.splitNodePath(nodePath)
|
||||||
if parts == nil {
|
if parts == nil {
|
||||||
return rootNode, nil
|
return rootNode, nil
|
||||||
}
|
}
|
||||||
|
@ -320,7 +326,7 @@ func (f *Fs) mkdir(rootNode *mega.Node, dir string) (node *mega.Node, err error)
|
||||||
f.mkdirMu.Lock()
|
f.mkdirMu.Lock()
|
||||||
defer f.mkdirMu.Unlock()
|
defer f.mkdirMu.Unlock()
|
||||||
|
|
||||||
parts := splitNodePath(dir)
|
parts := f.splitNodePath(dir)
|
||||||
if parts == nil {
|
if parts == nil {
|
||||||
return rootNode, nil
|
return rootNode, nil
|
||||||
}
|
}
|
||||||
|
@ -422,7 +428,7 @@ func (f *Fs) CleanUp(ctx context.Context) (err error) {
|
||||||
errors := 0
|
errors := 0
|
||||||
// similar to f.deleteNode(trash) but with HardDelete as true
|
// similar to f.deleteNode(trash) but with HardDelete as true
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
fs.Debugf(f, "Deleting trash %q", enc.ToStandardName(item.GetName()))
|
fs.Debugf(f, "Deleting trash %q", f.opt.Enc.ToStandardName(item.GetName()))
|
||||||
deleteErr := f.pacer.Call(func() (bool, error) {
|
deleteErr := f.pacer.Call(func() (bool, error) {
|
||||||
err := f.srv.Delete(item, true)
|
err := f.srv.Delete(item, true)
|
||||||
return shouldRetry(err)
|
return shouldRetry(err)
|
||||||
|
@ -504,7 +510,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
}
|
}
|
||||||
var iErr error
|
var iErr error
|
||||||
_, err = f.list(ctx, dirNode, func(info *mega.Node) bool {
|
_, err = f.list(ctx, dirNode, func(info *mega.Node) bool {
|
||||||
remote := path.Join(dir, enc.ToStandardName(info.GetName()))
|
remote := path.Join(dir, f.opt.Enc.ToStandardName(info.GetName()))
|
||||||
switch info.GetType() {
|
switch info.GetType() {
|
||||||
case mega.FOLDER, mega.ROOT, mega.INBOX, mega.TRASH:
|
case mega.FOLDER, mega.ROOT, mega.INBOX, mega.TRASH:
|
||||||
d := fs.NewDir(remote, info.GetTimeStamp()).SetID(info.GetHash())
|
d := fs.NewDir(remote, info.GetTimeStamp()).SetID(info.GetHash())
|
||||||
|
@ -726,7 +732,7 @@ func (f *Fs) move(dstRemote string, srcFs *Fs, srcRemote string, info *mega.Node
|
||||||
if srcLeaf != dstLeaf {
|
if srcLeaf != dstLeaf {
|
||||||
//log.Printf("rename %q to %q", srcLeaf, dstLeaf)
|
//log.Printf("rename %q to %q", srcLeaf, dstLeaf)
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
err = f.srv.Rename(info, enc.FromStandardName(dstLeaf))
|
err = f.srv.Rename(info, f.opt.Enc.FromStandardName(dstLeaf))
|
||||||
return shouldRetry(err)
|
return shouldRetry(err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -875,13 +881,13 @@ func (f *Fs) MergeDirs(ctx context.Context, dirs []fs.Directory) error {
|
||||||
}
|
}
|
||||||
// move them into place
|
// move them into place
|
||||||
for _, info := range infos {
|
for _, info := range infos {
|
||||||
fs.Infof(srcDir, "merging %q", enc.ToStandardName(info.GetName()))
|
fs.Infof(srcDir, "merging %q", f.opt.Enc.ToStandardName(info.GetName()))
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
err = f.srv.Move(info, dstDirNode)
|
err = f.srv.Move(info, dstDirNode)
|
||||||
return shouldRetry(err)
|
return shouldRetry(err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "MergeDirs move failed on %q in %v", enc.ToStandardName(info.GetName()), srcDir)
|
return errors.Wrapf(err, "MergeDirs move failed on %q in %v", f.opt.Enc.ToStandardName(info.GetName()), srcDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// rmdir (into trash) the now empty source directory
|
// rmdir (into trash) the now empty source directory
|
||||||
|
@ -1124,7 +1130,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||||
|
|
||||||
var u *mega.Upload
|
var u *mega.Upload
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
u, err = o.fs.srv.NewUpload(dirNode, enc.FromStandardName(leaf), size)
|
u, err = o.fs.srv.NewUpload(dirNode, o.fs.opt.Enc.FromStandardName(leaf), size)
|
||||||
return shouldRetry(err)
|
return shouldRetry(err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/lib/atexit"
|
"github.com/rclone/rclone/lib/atexit"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/readers"
|
"github.com/rclone/rclone/lib/readers"
|
||||||
|
@ -35,8 +36,6 @@ import (
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.OneDrive
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
rcloneClientID = "b15665d9-eda6-4092-8539-0eec376afd59"
|
rcloneClientID = "b15665d9-eda6-4092-8539-0eec376afd59"
|
||||||
rcloneEncryptedClientSecret = "_JUdzh3LnKNqSPcf4Wu5fgMFIQOI8glZu_akYgR8yf6egowNBg-R"
|
rcloneEncryptedClientSecret = "_JUdzh3LnKNqSPcf4Wu5fgMFIQOI8glZu_akYgR8yf6egowNBg-R"
|
||||||
|
@ -252,6 +251,11 @@ delete OneNote files or otherwise want them to show up in directory
|
||||||
listing, set this option.`,
|
listing, set this option.`,
|
||||||
Default: false,
|
Default: false,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.OneDrive,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -262,6 +266,7 @@ type Options struct {
|
||||||
DriveID string `config:"drive_id"`
|
DriveID string `config:"drive_id"`
|
||||||
DriveType string `config:"drive_type"`
|
DriveType string `config:"drive_type"`
|
||||||
ExposeOneNoteFiles bool `config:"expose_onenote_files"`
|
ExposeOneNoteFiles bool `config:"expose_onenote_files"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote one drive
|
// Fs represents a remote one drive
|
||||||
|
@ -355,7 +360,7 @@ func shouldRetry(resp *http.Response, err error) (bool, error) {
|
||||||
// If `relPath` == '', do not append the slash (See #3664)
|
// If `relPath` == '', do not append the slash (See #3664)
|
||||||
func (f *Fs) readMetaDataForPathRelativeToID(ctx context.Context, normalizedID string, relPath string) (info *api.Item, resp *http.Response, err error) {
|
func (f *Fs) readMetaDataForPathRelativeToID(ctx context.Context, normalizedID string, relPath string) (info *api.Item, resp *http.Response, err error) {
|
||||||
if relPath != "" {
|
if relPath != "" {
|
||||||
relPath = "/" + withTrailingColon(rest.URLPathEscape(enc.FromStandardPath(relPath)))
|
relPath = "/" + withTrailingColon(rest.URLPathEscape(f.opt.Enc.FromStandardPath(relPath)))
|
||||||
}
|
}
|
||||||
opts := newOptsCall(normalizedID, "GET", ":"+relPath)
|
opts := newOptsCall(normalizedID, "GET", ":"+relPath)
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
@ -380,7 +385,7 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string) (info *api.It
|
||||||
} else {
|
} else {
|
||||||
opts = rest.Opts{
|
opts = rest.Opts{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Path: "/root:/" + rest.URLPathEscape(enc.FromStandardPath(path)),
|
Path: "/root:/" + rest.URLPathEscape(f.opt.Enc.FromStandardPath(path)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
@ -628,7 +633,7 @@ func (f *Fs) CreateDir(ctx context.Context, dirID, leaf string) (newID string, e
|
||||||
var info *api.Item
|
var info *api.Item
|
||||||
opts := newOptsCall(dirID, "POST", "/children")
|
opts := newOptsCall(dirID, "POST", "/children")
|
||||||
mkdir := api.CreateItemRequest{
|
mkdir := api.CreateItemRequest{
|
||||||
Name: enc.FromStandardName(leaf),
|
Name: f.opt.Enc.FromStandardName(leaf),
|
||||||
ConflictBehavior: "fail",
|
ConflictBehavior: "fail",
|
||||||
}
|
}
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
@ -688,7 +693,7 @@ OUTER:
|
||||||
if item.Deleted != nil {
|
if item.Deleted != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
item.Name = enc.ToStandardName(item.GetName())
|
item.Name = f.opt.Enc.ToStandardName(item.GetName())
|
||||||
if fn(item) {
|
if fn(item) {
|
||||||
found = true
|
found = true
|
||||||
break OUTER
|
break OUTER
|
||||||
|
@ -944,7 +949,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
|
|
||||||
id, dstDriveID, _ := parseNormalizedID(directoryID)
|
id, dstDriveID, _ := parseNormalizedID(directoryID)
|
||||||
|
|
||||||
replacedLeaf := enc.FromStandardName(leaf)
|
replacedLeaf := f.opt.Enc.FromStandardName(leaf)
|
||||||
copyReq := api.CopyItemRequest{
|
copyReq := api.CopyItemRequest{
|
||||||
Name: &replacedLeaf,
|
Name: &replacedLeaf,
|
||||||
ParentReference: api.ItemReference{
|
ParentReference: api.ItemReference{
|
||||||
|
@ -1028,7 +1033,7 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
opts := newOptsCall(srcObj.id, "PATCH", "")
|
opts := newOptsCall(srcObj.id, "PATCH", "")
|
||||||
|
|
||||||
move := api.MoveItemRequest{
|
move := api.MoveItemRequest{
|
||||||
Name: enc.FromStandardName(leaf),
|
Name: f.opt.Enc.FromStandardName(leaf),
|
||||||
ParentReference: &api.ItemReference{
|
ParentReference: &api.ItemReference{
|
||||||
DriveID: dstDriveID,
|
DriveID: dstDriveID,
|
||||||
ID: id,
|
ID: id,
|
||||||
|
@ -1143,7 +1148,7 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string
|
||||||
// Do the move
|
// Do the move
|
||||||
opts := newOptsCall(srcID, "PATCH", "")
|
opts := newOptsCall(srcID, "PATCH", "")
|
||||||
move := api.MoveItemRequest{
|
move := api.MoveItemRequest{
|
||||||
Name: enc.FromStandardName(leaf),
|
Name: f.opt.Enc.FromStandardName(leaf),
|
||||||
ParentReference: &api.ItemReference{
|
ParentReference: &api.ItemReference{
|
||||||
DriveID: dstDriveID,
|
DriveID: dstDriveID,
|
||||||
ID: parsedDstDirID,
|
ID: parsedDstDirID,
|
||||||
|
@ -1265,7 +1270,7 @@ func (o *Object) rootPath() string {
|
||||||
|
|
||||||
// srvPath returns a path for use in server given a remote
|
// srvPath returns a path for use in server given a remote
|
||||||
func (f *Fs) srvPath(remote string) string {
|
func (f *Fs) srvPath(remote string) string {
|
||||||
return enc.FromStandardPath(f.rootSlash() + remote)
|
return f.opt.Enc.FromStandardPath(f.rootSlash() + remote)
|
||||||
}
|
}
|
||||||
|
|
||||||
// srvPath returns a path for use in server
|
// srvPath returns a path for use in server
|
||||||
|
@ -1377,7 +1382,7 @@ func (o *Object) setModTime(ctx context.Context, modTime time.Time) (*api.Item,
|
||||||
opts = rest.Opts{
|
opts = rest.Opts{
|
||||||
Method: "PATCH",
|
Method: "PATCH",
|
||||||
RootURL: rootURL,
|
RootURL: rootURL,
|
||||||
Path: "/" + drive + "/items/" + trueDirID + ":/" + withTrailingColon(rest.URLPathEscape(enc.FromStandardName(leaf))),
|
Path: "/" + drive + "/items/" + trueDirID + ":/" + withTrailingColon(rest.URLPathEscape(o.fs.opt.Enc.FromStandardName(leaf))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
opts = rest.Opts{
|
opts = rest.Opts{
|
||||||
|
@ -1452,7 +1457,7 @@ func (o *Object) createUploadSession(ctx context.Context, modTime time.Time) (re
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
RootURL: rootURL,
|
RootURL: rootURL,
|
||||||
Path: fmt.Sprintf("/%s/items/%s:/%s:/createUploadSession",
|
Path: fmt.Sprintf("/%s/items/%s:/%s:/createUploadSession",
|
||||||
drive, id, rest.URLPathEscape(enc.FromStandardName(leaf))),
|
drive, id, rest.URLPathEscape(o.fs.opt.Enc.FromStandardName(leaf))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
opts = rest.Opts{
|
opts = rest.Opts{
|
||||||
|
@ -1604,7 +1609,7 @@ func (o *Object) uploadSinglepart(ctx context.Context, in io.Reader, size int64,
|
||||||
opts = rest.Opts{
|
opts = rest.Opts{
|
||||||
Method: "PUT",
|
Method: "PUT",
|
||||||
RootURL: rootURL,
|
RootURL: rootURL,
|
||||||
Path: "/" + drive + "/items/" + trueDirID + ":/" + rest.URLPathEscape(enc.FromStandardName(leaf)) + ":/content",
|
Path: "/" + drive + "/items/" + trueDirID + ":/" + rest.URLPathEscape(o.fs.opt.Enc.FromStandardName(leaf)) + ":/content",
|
||||||
ContentLength: &size,
|
ContentLength: &size,
|
||||||
Body: in,
|
Body: in,
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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"
|
||||||
|
@ -21,13 +22,12 @@ import (
|
||||||
"github.com/rclone/rclone/fs/fshttp"
|
"github.com/rclone/rclone/fs/fshttp"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/readers"
|
"github.com/rclone/rclone/lib/readers"
|
||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.OpenDrive
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultEndpoint = "https://dev.opendrive.com/api/v1"
|
defaultEndpoint = "https://dev.opendrive.com/api/v1"
|
||||||
minSleep = 10 * time.Millisecond
|
minSleep = 10 * time.Millisecond
|
||||||
|
@ -50,6 +50,11 @@ func init() {
|
||||||
Help: "Password.",
|
Help: "Password.",
|
||||||
IsPassword: true,
|
IsPassword: true,
|
||||||
Required: true,
|
Required: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.OpenDrive,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -58,6 +63,7 @@ func init() {
|
||||||
type Options struct {
|
type Options struct {
|
||||||
UserName string `config:"username"`
|
UserName string `config:"username"`
|
||||||
Password string `config:"password"`
|
Password string `config:"password"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote server
|
// Fs represents a remote server
|
||||||
|
@ -588,7 +594,7 @@ func (f *Fs) createObject(ctx context.Context, remote string, modTime time.Time,
|
||||||
fs: f,
|
fs: f,
|
||||||
remote: remote,
|
remote: remote,
|
||||||
}
|
}
|
||||||
return o, enc.FromStandardName(leaf), directoryID, nil
|
return o, f.opt.Enc.FromStandardName(leaf), directoryID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readMetaDataForPath reads the metadata from the path
|
// readMetaDataForPath reads the metadata from the path
|
||||||
|
@ -690,7 +696,7 @@ func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string,
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
createDirData := createFolder{
|
createDirData := createFolder{
|
||||||
SessionID: f.session.SessionID,
|
SessionID: f.session.SessionID,
|
||||||
FolderName: enc.FromStandardName(leaf),
|
FolderName: f.opt.Enc.FromStandardName(leaf),
|
||||||
FolderSubParent: pathID,
|
FolderSubParent: pathID,
|
||||||
FolderIsPublic: 0,
|
FolderIsPublic: 0,
|
||||||
FolderPublicUpl: 0,
|
FolderPublicUpl: 0,
|
||||||
|
@ -736,7 +742,7 @@ func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut strin
|
||||||
return "", false, errors.Wrap(err, "failed to get folder list")
|
return "", false, errors.Wrap(err, "failed to get folder list")
|
||||||
}
|
}
|
||||||
|
|
||||||
leaf = enc.FromStandardName(leaf)
|
leaf = f.opt.Enc.FromStandardName(leaf)
|
||||||
for _, folder := range folderList.Folders {
|
for _, folder := range folderList.Folders {
|
||||||
// fs.Debugf(nil, "Folder: %s (%s)", folder.Name, folder.FolderID)
|
// fs.Debugf(nil, "Folder: %s (%s)", folder.Name, folder.FolderID)
|
||||||
|
|
||||||
|
@ -784,7 +790,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, folder := range folderList.Folders {
|
for _, folder := range folderList.Folders {
|
||||||
folder.Name = enc.ToStandardName(folder.Name)
|
folder.Name = f.opt.Enc.ToStandardName(folder.Name)
|
||||||
// fs.Debugf(nil, "Folder: %s (%s)", folder.Name, folder.FolderID)
|
// fs.Debugf(nil, "Folder: %s (%s)", folder.Name, folder.FolderID)
|
||||||
remote := path.Join(dir, folder.Name)
|
remote := path.Join(dir, folder.Name)
|
||||||
// cache the directory ID for later lookups
|
// cache the directory ID for later lookups
|
||||||
|
@ -795,7 +801,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, file := range folderList.Files {
|
for _, file := range folderList.Files {
|
||||||
file.Name = enc.ToStandardName(file.Name)
|
file.Name = f.opt.Enc.ToStandardName(file.Name)
|
||||||
// fs.Debugf(nil, "File: %s (%s)", file.Name, file.FileID)
|
// fs.Debugf(nil, "File: %s (%s)", file.Name, file.FileID)
|
||||||
remote := path.Join(dir, file.Name)
|
remote := path.Join(dir, file.Name)
|
||||||
o, err := f.newObjectWithInfo(ctx, remote, &file)
|
o, err := f.newObjectWithInfo(ctx, remote, &file)
|
||||||
|
@ -1050,7 +1056,7 @@ func (o *Object) readMetaData(ctx context.Context) (err error) {
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Path: fmt.Sprintf("/folder/itembyname.json/%s/%s?name=%s",
|
Path: fmt.Sprintf("/folder/itembyname.json/%s/%s?name=%s",
|
||||||
o.fs.session.SessionID, directoryID, url.QueryEscape(enc.FromStandardName(leaf))),
|
o.fs.session.SessionID, directoryID, url.QueryEscape(o.fs.opt.Enc.FromStandardName(leaf))),
|
||||||
}
|
}
|
||||||
resp, err = o.fs.srv.CallJSON(ctx, &opts, nil, &folderList)
|
resp, err = o.fs.srv.CallJSON(ctx, &opts, nil, &folderList)
|
||||||
return o.fs.shouldRetry(resp, err)
|
return o.fs.shouldRetry(resp, err)
|
||||||
|
|
|
@ -30,14 +30,13 @@ import (
|
||||||
"github.com/rclone/rclone/fs/fserrors"
|
"github.com/rclone/rclone/fs/fserrors"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.Pcloud
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
rcloneClientID = "DnONSzyJXpm"
|
rcloneClientID = "DnONSzyJXpm"
|
||||||
rcloneEncryptedClientSecret = "ej1OIF39VOQQ0PXaSdK9ztkLw3tdLNscW2157TKNQdQKkICR4uU7aFg4eFM"
|
rcloneEncryptedClientSecret = "ej1OIF39VOQQ0PXaSdK9ztkLw3tdLNscW2157TKNQdQKkICR4uU7aFg4eFM"
|
||||||
|
@ -81,12 +80,18 @@ func init() {
|
||||||
}, {
|
}, {
|
||||||
Name: config.ConfigClientSecret,
|
Name: config.ConfigClientSecret,
|
||||||
Help: "Pcloud App Client Secret\nLeave blank normally.",
|
Help: "Pcloud App Client Secret\nLeave blank normally.",
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Pcloud,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options defines the configuration for this backend
|
// Options defines the configuration for this backend
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote pcloud
|
// Fs represents a remote pcloud
|
||||||
|
@ -342,7 +347,7 @@ func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string,
|
||||||
Path: "/createfolder",
|
Path: "/createfolder",
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
}
|
}
|
||||||
opts.Parameters.Set("name", enc.FromStandardName(leaf))
|
opts.Parameters.Set("name", f.opt.Enc.FromStandardName(leaf))
|
||||||
opts.Parameters.Set("folderid", dirIDtoNumber(pathID))
|
opts.Parameters.Set("folderid", dirIDtoNumber(pathID))
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
|
resp, err = f.srv.CallJSON(ctx, &opts, nil, &result)
|
||||||
|
@ -418,7 +423,7 @@ func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, fi
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item.Name = enc.ToStandardName(item.Name)
|
item.Name = f.opt.Enc.ToStandardName(item.Name)
|
||||||
if fn(item) {
|
if fn(item) {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
|
@ -610,7 +615,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
}
|
}
|
||||||
opts.Parameters.Set("fileid", fileIDtoNumber(srcObj.id))
|
opts.Parameters.Set("fileid", fileIDtoNumber(srcObj.id))
|
||||||
opts.Parameters.Set("toname", enc.FromStandardName(leaf))
|
opts.Parameters.Set("toname", f.opt.Enc.FromStandardName(leaf))
|
||||||
opts.Parameters.Set("tofolderid", dirIDtoNumber(directoryID))
|
opts.Parameters.Set("tofolderid", dirIDtoNumber(directoryID))
|
||||||
opts.Parameters.Set("mtime", fmt.Sprintf("%d", srcObj.modTime.Unix()))
|
opts.Parameters.Set("mtime", fmt.Sprintf("%d", srcObj.modTime.Unix()))
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
@ -689,7 +694,7 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object,
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
}
|
}
|
||||||
opts.Parameters.Set("fileid", fileIDtoNumber(srcObj.id))
|
opts.Parameters.Set("fileid", fileIDtoNumber(srcObj.id))
|
||||||
opts.Parameters.Set("toname", enc.FromStandardName(leaf))
|
opts.Parameters.Set("toname", f.opt.Enc.FromStandardName(leaf))
|
||||||
opts.Parameters.Set("tofolderid", dirIDtoNumber(directoryID))
|
opts.Parameters.Set("tofolderid", dirIDtoNumber(directoryID))
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
var result api.ItemResult
|
var result api.ItemResult
|
||||||
|
@ -786,7 +791,7 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
}
|
}
|
||||||
opts.Parameters.Set("folderid", dirIDtoNumber(srcID))
|
opts.Parameters.Set("folderid", dirIDtoNumber(srcID))
|
||||||
opts.Parameters.Set("toname", enc.FromStandardName(leaf))
|
opts.Parameters.Set("toname", f.opt.Enc.FromStandardName(leaf))
|
||||||
opts.Parameters.Set("tofolderid", dirIDtoNumber(directoryID))
|
opts.Parameters.Set("tofolderid", dirIDtoNumber(directoryID))
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
var result api.ItemResult
|
var result api.ItemResult
|
||||||
|
@ -1066,7 +1071,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
TransferEncoding: []string{"identity"}, // pcloud doesn't like chunked encoding
|
TransferEncoding: []string{"identity"}, // pcloud doesn't like chunked encoding
|
||||||
}
|
}
|
||||||
leaf = enc.FromStandardName(leaf)
|
leaf = o.fs.opt.Enc.FromStandardName(leaf)
|
||||||
opts.Parameters.Set("filename", leaf)
|
opts.Parameters.Set("filename", leaf)
|
||||||
opts.Parameters.Set("folderid", dirIDtoNumber(directoryID))
|
opts.Parameters.Set("folderid", dirIDtoNumber(directoryID))
|
||||||
opts.Parameters.Set("nopartial", "1")
|
opts.Parameters.Set("nopartial", "1")
|
||||||
|
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/backend/premiumizeme/api"
|
"github.com/rclone/rclone/backend/premiumizeme/api"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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"
|
||||||
|
@ -39,6 +40,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/fshttp"
|
"github.com/rclone/rclone/fs/fshttp"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/random"
|
"github.com/rclone/rclone/lib/random"
|
||||||
|
@ -46,8 +48,6 @@ import (
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.PremiumizeMe
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
rcloneClientID = "658922194"
|
rcloneClientID = "658922194"
|
||||||
rcloneEncryptedClientSecret = "B5YIvQoRIhcpAYs8HYeyjb9gK-ftmZEbqdh_gNfc4RgO9Q"
|
rcloneEncryptedClientSecret = "B5YIvQoRIhcpAYs8HYeyjb9gK-ftmZEbqdh_gNfc4RgO9Q"
|
||||||
|
@ -93,6 +93,11 @@ This is not normally used - use oauth instead.
|
||||||
`,
|
`,
|
||||||
Hide: fs.OptionHideBoth,
|
Hide: fs.OptionHideBoth,
|
||||||
Default: "",
|
Default: "",
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.PremiumizeMe,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -100,6 +105,7 @@ This is not normally used - use oauth instead.
|
||||||
// Options defines the configuration for this backend
|
// Options defines the configuration for this backend
|
||||||
type Options struct {
|
type Options struct {
|
||||||
APIKey string `config:"api_key"`
|
APIKey string `config:"api_key"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote cloud storage system
|
// Fs represents a remote cloud storage system
|
||||||
|
@ -364,7 +370,7 @@ func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string,
|
||||||
Path: "/folder/create",
|
Path: "/folder/create",
|
||||||
Parameters: f.baseParams(),
|
Parameters: f.baseParams(),
|
||||||
MultipartParams: url.Values{
|
MultipartParams: url.Values{
|
||||||
"name": {enc.FromStandardName(leaf)},
|
"name": {f.opt.Enc.FromStandardName(leaf)},
|
||||||
"parent_id": {pathID},
|
"parent_id": {pathID},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -429,7 +435,7 @@ func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, fi
|
||||||
fs.Debugf(f, "Ignoring %q - unknown type %q", item.Name, item.Type)
|
fs.Debugf(f, "Ignoring %q - unknown type %q", item.Name, item.Type)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
item.Name = enc.ToStandardName(item.Name)
|
item.Name = f.opt.Enc.ToStandardName(item.Name)
|
||||||
if fn(item) {
|
if fn(item) {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
|
@ -637,8 +643,8 @@ func (f *Fs) Purge(ctx context.Context) error {
|
||||||
// between directories and a separate one to rename them. We try to
|
// between directories and a separate one to rename them. We try to
|
||||||
// call the minimum number of API calls.
|
// call the minimum number of API calls.
|
||||||
func (f *Fs) move(ctx context.Context, isFile bool, id, oldLeaf, newLeaf, oldDirectoryID, newDirectoryID string) (err error) {
|
func (f *Fs) move(ctx context.Context, isFile bool, id, oldLeaf, newLeaf, oldDirectoryID, newDirectoryID string) (err error) {
|
||||||
newLeaf = enc.FromStandardName(newLeaf)
|
newLeaf = f.opt.Enc.FromStandardName(newLeaf)
|
||||||
oldLeaf = enc.FromStandardName(oldLeaf)
|
oldLeaf = f.opt.Enc.FromStandardName(oldLeaf)
|
||||||
doRenameLeaf := oldLeaf != newLeaf
|
doRenameLeaf := oldLeaf != newLeaf
|
||||||
doMove := oldDirectoryID != newDirectoryID
|
doMove := oldDirectoryID != newDirectoryID
|
||||||
|
|
||||||
|
@ -891,7 +897,7 @@ func (o *Object) Remote() string {
|
||||||
|
|
||||||
// srvPath returns a path for use in server
|
// srvPath returns a path for use in server
|
||||||
func (o *Object) srvPath() string {
|
func (o *Object) srvPath() string {
|
||||||
return enc.FromStandardPath(o.fs.rootSlash() + o.remote)
|
return o.fs.opt.Enc.FromStandardPath(o.fs.rootSlash() + o.remote)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns the SHA-1 of an object returning a lowercase hex string
|
// Hash returns the SHA-1 of an object returning a lowercase hex string
|
||||||
|
@ -1006,7 +1012,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
leaf = enc.FromStandardName(leaf)
|
leaf = o.fs.opt.Enc.FromStandardName(leaf)
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
var info api.FolderUploadinfoResponse
|
var info api.FolderUploadinfoResponse
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/putdotio/go-putio/putio"
|
"github.com/putdotio/go-putio/putio"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
"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/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
|
@ -29,6 +30,7 @@ type Fs struct {
|
||||||
name string // name of this remote
|
name string // name of this remote
|
||||||
root string // the path we are working on
|
root string // the path we are working on
|
||||||
features *fs.Features // optional features
|
features *fs.Features // optional features
|
||||||
|
opt Options // options for this Fs
|
||||||
client *putio.Client // client for making API calls to Put.io
|
client *putio.Client // client for making API calls to Put.io
|
||||||
pacer *fs.Pacer // To pace the API calls
|
pacer *fs.Pacer // To pace the API calls
|
||||||
dirCache *dircache.DirCache // Map of directory path to directory id
|
dirCache *dircache.DirCache // Map of directory path to directory id
|
||||||
|
@ -60,6 +62,12 @@ func (f *Fs) Features() *fs.Features {
|
||||||
// NewFs constructs an Fs from the path, container:path
|
// NewFs constructs an Fs from the path, container:path
|
||||||
func NewFs(name, root string, m configmap.Mapper) (f fs.Fs, err error) {
|
func NewFs(name, root string, m configmap.Mapper) (f fs.Fs, err error) {
|
||||||
// defer log.Trace(name, "root=%v", root)("f=%+v, err=%v", &f, &err)
|
// defer log.Trace(name, "root=%v", root)("f=%+v, err=%v", &f, &err)
|
||||||
|
// Parse config into Options struct
|
||||||
|
opt := new(Options)
|
||||||
|
err = configstruct.Set(m, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
oAuthClient, _, err := oauthutil.NewClient(name, m, putioConfig)
|
oAuthClient, _, err := oauthutil.NewClient(name, m, putioConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to configure putio")
|
return nil, errors.Wrap(err, "failed to configure putio")
|
||||||
|
@ -67,6 +75,7 @@ func NewFs(name, root string, m configmap.Mapper) (f fs.Fs, err error) {
|
||||||
p := &Fs{
|
p := &Fs{
|
||||||
name: name,
|
name: name,
|
||||||
root: root,
|
root: root,
|
||||||
|
opt: *opt,
|
||||||
pacer: fs.NewPacer(pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))),
|
pacer: fs.NewPacer(pacer.NewDefault(pacer.MinSleep(minSleep), pacer.MaxSleep(maxSleep), pacer.DecayConstant(decayConstant))),
|
||||||
client: putio.NewClient(oAuthClient),
|
client: putio.NewClient(oAuthClient),
|
||||||
oAuthClient: oAuthClient,
|
oAuthClient: oAuthClient,
|
||||||
|
@ -127,7 +136,7 @@ func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string,
|
||||||
var entry putio.File
|
var entry putio.File
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
// fs.Debugf(f, "creating folder. part: %s, parentID: %d", leaf, parentID)
|
// fs.Debugf(f, "creating folder. part: %s, parentID: %d", leaf, parentID)
|
||||||
entry, err = f.client.Files.CreateFolder(ctx, enc.FromStandardName(leaf), parentID)
|
entry, err = f.client.Files.CreateFolder(ctx, f.opt.Enc.FromStandardName(leaf), parentID)
|
||||||
return shouldRetry(err)
|
return shouldRetry(err)
|
||||||
})
|
})
|
||||||
return itoa(entry.ID), err
|
return itoa(entry.ID), err
|
||||||
|
@ -154,7 +163,7 @@ func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut strin
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, child := range children {
|
for _, child := range children {
|
||||||
if enc.ToStandardName(child.Name) == leaf {
|
if f.opt.Enc.ToStandardName(child.Name) == leaf {
|
||||||
found = true
|
found = true
|
||||||
pathIDOut = itoa(child.ID)
|
pathIDOut = itoa(child.ID)
|
||||||
if !child.IsDir() {
|
if !child.IsDir() {
|
||||||
|
@ -196,7 +205,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, child := range children {
|
for _, child := range children {
|
||||||
remote := path.Join(dir, enc.ToStandardName(child.Name))
|
remote := path.Join(dir, f.opt.Enc.ToStandardName(child.Name))
|
||||||
// fs.Debugf(f, "child: %s", remote)
|
// fs.Debugf(f, "child: %s", remote)
|
||||||
if child.IsDir() {
|
if child.IsDir() {
|
||||||
f.dirCache.Put(remote, itoa(child.ID))
|
f.dirCache.Put(remote, itoa(child.ID))
|
||||||
|
@ -274,7 +283,7 @@ func (f *Fs) createUpload(ctx context.Context, name string, size int64, parentID
|
||||||
req = req.WithContext(ctx) // go1.13 can use NewRequestWithContext
|
req = req.WithContext(ctx) // go1.13 can use NewRequestWithContext
|
||||||
req.Header.Set("tus-resumable", "1.0.0")
|
req.Header.Set("tus-resumable", "1.0.0")
|
||||||
req.Header.Set("upload-length", strconv.FormatInt(size, 10))
|
req.Header.Set("upload-length", strconv.FormatInt(size, 10))
|
||||||
b64name := base64.StdEncoding.EncodeToString([]byte(enc.FromStandardName(name)))
|
b64name := base64.StdEncoding.EncodeToString([]byte(f.opt.Enc.FromStandardName(name)))
|
||||||
b64true := base64.StdEncoding.EncodeToString([]byte("true"))
|
b64true := base64.StdEncoding.EncodeToString([]byte("true"))
|
||||||
b64parentID := base64.StdEncoding.EncodeToString([]byte(parentID))
|
b64parentID := base64.StdEncoding.EncodeToString([]byte(parentID))
|
||||||
b64modifiedAt := base64.StdEncoding.EncodeToString([]byte(modTime.Format(time.RFC3339)))
|
b64modifiedAt := base64.StdEncoding.EncodeToString([]byte(modTime.Format(time.RFC3339)))
|
||||||
|
@ -546,7 +555,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (o fs.Objec
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
params.Set("file_id", strconv.FormatInt(srcObj.file.ID, 10))
|
params.Set("file_id", strconv.FormatInt(srcObj.file.ID, 10))
|
||||||
params.Set("parent_id", directoryID)
|
params.Set("parent_id", directoryID)
|
||||||
params.Set("name", enc.FromStandardName(leaf))
|
params.Set("name", f.opt.Enc.FromStandardName(leaf))
|
||||||
req, err := f.client.NewRequest(ctx, "POST", "/v2/files/copy", strings.NewReader(params.Encode()))
|
req, err := f.client.NewRequest(ctx, "POST", "/v2/files/copy", strings.NewReader(params.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -585,7 +594,7 @@ func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (o fs.Objec
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
params.Set("file_id", strconv.FormatInt(srcObj.file.ID, 10))
|
params.Set("file_id", strconv.FormatInt(srcObj.file.ID, 10))
|
||||||
params.Set("parent_id", directoryID)
|
params.Set("parent_id", directoryID)
|
||||||
params.Set("name", enc.FromStandardName(leaf))
|
params.Set("name", f.opt.Enc.FromStandardName(leaf))
|
||||||
req, err := f.client.NewRequest(ctx, "POST", "/v2/files/move", strings.NewReader(params.Encode()))
|
req, err := f.client.NewRequest(ctx, "POST", "/v2/files/move", strings.NewReader(params.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -674,7 +683,7 @@ func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
params.Set("file_id", srcID)
|
params.Set("file_id", srcID)
|
||||||
params.Set("parent_id", dstDirectoryID)
|
params.Set("parent_id", dstDirectoryID)
|
||||||
params.Set("name", enc.FromStandardName(leaf))
|
params.Set("name", f.opt.Enc.FromStandardName(leaf))
|
||||||
req, err := f.client.NewRequest(ctx, "POST", "/v2/files/move", strings.NewReader(params.Encode()))
|
req, err := f.client.NewRequest(ctx, "POST", "/v2/files/move", strings.NewReader(params.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
|
@ -137,7 +137,7 @@ func (o *Object) readEntry(ctx context.Context) (f *putio.File, err error) {
|
||||||
}
|
}
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
// fs.Debugf(o, "requesting child. directoryID: %s, name: %s", directoryID, leaf)
|
// fs.Debugf(o, "requesting child. directoryID: %s, name: %s", directoryID, leaf)
|
||||||
req, err := o.fs.client.NewRequest(ctx, "GET", "/v2/files/"+directoryID+"/child?name="+url.QueryEscape(enc.FromStandardName(leaf)), nil)
|
req, err := o.fs.client.NewRequest(ctx, "GET", "/v2/files/"+directoryID+"/child?name="+url.QueryEscape(o.fs.opt.Enc.FromStandardName(leaf)), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"github.com/rclone/rclone/fs/config/configmap"
|
"github.com/rclone/rclone/fs/config/configmap"
|
||||||
"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/encodings"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
@ -25,7 +27,6 @@ canReadUnnormalized = true
|
||||||
canReadRenormalized = true
|
canReadRenormalized = true
|
||||||
canStream = false
|
canStream = false
|
||||||
*/
|
*/
|
||||||
const enc = encodings.Putio
|
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const (
|
const (
|
||||||
|
@ -65,9 +66,20 @@ func init() {
|
||||||
log.Fatalf("Failed to configure token: %v", err)
|
log.Fatalf("Failed to configure token: %v", err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Options: []fs.Option{{
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Putio,
|
||||||
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Options defines the configuration for this backend
|
||||||
|
type Options struct {
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
|
}
|
||||||
|
|
||||||
// Check the interfaces are satisfied
|
// Check the interfaces are satisfied
|
||||||
var (
|
var (
|
||||||
_ fs.Fs = (*Fs)(nil)
|
_ fs.Fs = (*Fs)(nil)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
|
@ -25,13 +26,12 @@ import (
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/fs/walk"
|
"github.com/rclone/rclone/fs/walk"
|
||||||
"github.com/rclone/rclone/lib/bucket"
|
"github.com/rclone/rclone/lib/bucket"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
qsConfig "github.com/yunify/qingstor-sdk-go/v3/config"
|
qsConfig "github.com/yunify/qingstor-sdk-go/v3/config"
|
||||||
qsErr "github.com/yunify/qingstor-sdk-go/v3/request/errors"
|
qsErr "github.com/yunify/qingstor-sdk-go/v3/request/errors"
|
||||||
qs "github.com/yunify/qingstor-sdk-go/v3/service"
|
qs "github.com/yunify/qingstor-sdk-go/v3/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.QingStor
|
|
||||||
|
|
||||||
// Register with Fs
|
// Register with Fs
|
||||||
func init() {
|
func init() {
|
||||||
fs.Register(&fs.RegInfo{
|
fs.Register(&fs.RegInfo{
|
||||||
|
@ -113,6 +113,11 @@ and these uploads do not fully utilize your bandwidth, then increasing
|
||||||
this may help to speed up the transfers.`,
|
this may help to speed up the transfers.`,
|
||||||
Default: 1,
|
Default: 1,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.QingStor,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -145,6 +150,7 @@ type Options struct {
|
||||||
UploadCutoff fs.SizeSuffix `config:"upload_cutoff"`
|
UploadCutoff fs.SizeSuffix `config:"upload_cutoff"`
|
||||||
ChunkSize fs.SizeSuffix `config:"chunk_size"`
|
ChunkSize fs.SizeSuffix `config:"chunk_size"`
|
||||||
UploadConcurrency int `config:"upload_concurrency"`
|
UploadConcurrency int `config:"upload_concurrency"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote qingstor server
|
// Fs represents a remote qingstor server
|
||||||
|
@ -188,7 +194,7 @@ func parsePath(path string) (root string) {
|
||||||
// relative to f.root
|
// relative to f.root
|
||||||
func (f *Fs) split(rootRelativePath string) (bucketName, bucketPath string) {
|
func (f *Fs) split(rootRelativePath string) (bucketName, bucketPath string) {
|
||||||
bucketName, bucketPath = bucket.Split(path.Join(f.root, rootRelativePath))
|
bucketName, bucketPath = bucket.Split(path.Join(f.root, rootRelativePath))
|
||||||
return enc.FromStandardName(bucketName), enc.FromStandardPath(bucketPath)
|
return f.opt.Enc.FromStandardName(bucketName), f.opt.Enc.FromStandardPath(bucketPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// split returns bucket and bucketPath from the object
|
// split returns bucket and bucketPath from the object
|
||||||
|
@ -357,7 +363,7 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
encodedDirectory := enc.FromStandardPath(f.rootDirectory)
|
encodedDirectory := f.opt.Enc.FromStandardPath(f.rootDirectory)
|
||||||
_, err = bucketInit.HeadObject(encodedDirectory, &qs.HeadObjectInput{})
|
_, err = bucketInit.HeadObject(encodedDirectory, &qs.HeadObjectInput{})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
newRoot := path.Dir(f.root)
|
newRoot := path.Dir(f.root)
|
||||||
|
@ -555,7 +561,7 @@ func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBuck
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
remote := *commonPrefix
|
remote := *commonPrefix
|
||||||
remote = enc.ToStandardPath(remote)
|
remote = f.opt.Enc.ToStandardPath(remote)
|
||||||
if !strings.HasPrefix(remote, prefix) {
|
if !strings.HasPrefix(remote, prefix) {
|
||||||
fs.Logf(f, "Odd name received %q", remote)
|
fs.Logf(f, "Odd name received %q", remote)
|
||||||
continue
|
continue
|
||||||
|
@ -576,7 +582,7 @@ func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBuck
|
||||||
|
|
||||||
for _, object := range resp.Keys {
|
for _, object := range resp.Keys {
|
||||||
remote := qs.StringValue(object.Key)
|
remote := qs.StringValue(object.Key)
|
||||||
remote = enc.ToStandardPath(remote)
|
remote = f.opt.Enc.ToStandardPath(remote)
|
||||||
if !strings.HasPrefix(remote, prefix) {
|
if !strings.HasPrefix(remote, prefix) {
|
||||||
fs.Logf(f, "Odd name received %q", remote)
|
fs.Logf(f, "Odd name received %q", remote)
|
||||||
continue
|
continue
|
||||||
|
@ -653,7 +659,7 @@ func (f *Fs) listBuckets(ctx context.Context) (entries fs.DirEntries, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bucket := range resp.Buckets {
|
for _, bucket := range resp.Buckets {
|
||||||
d := fs.NewDir(enc.ToStandardName(qs.StringValue(bucket.Name)), qs.TimeValue(bucket.Created))
|
d := fs.NewDir(f.opt.Enc.ToStandardName(qs.StringValue(bucket.Name)), qs.TimeValue(bucket.Created))
|
||||||
entries = append(entries, d)
|
entries = append(entries, d)
|
||||||
}
|
}
|
||||||
return entries, nil
|
return entries, nil
|
||||||
|
|
|
@ -46,6 +46,7 @@ import (
|
||||||
"github.com/ncw/swift"
|
"github.com/ncw/swift"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
|
@ -54,14 +55,13 @@ import (
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/fs/walk"
|
"github.com/rclone/rclone/fs/walk"
|
||||||
"github.com/rclone/rclone/lib/bucket"
|
"github.com/rclone/rclone/lib/bucket"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/readers"
|
"github.com/rclone/rclone/lib/readers"
|
||||||
"github.com/rclone/rclone/lib/rest"
|
"github.com/rclone/rclone/lib/rest"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.S3
|
|
||||||
|
|
||||||
// Register with Fs
|
// Register with Fs
|
||||||
func init() {
|
func init() {
|
||||||
fs.Register(&fs.RegInfo{
|
fs.Register(&fs.RegInfo{
|
||||||
|
@ -811,6 +811,11 @@ In Ceph, this can be increased with the "rgw list buckets max chunk" option.
|
||||||
`,
|
`,
|
||||||
Default: 1000,
|
Default: 1000,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.S3,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -853,6 +858,7 @@ type Options struct {
|
||||||
UseAccelerateEndpoint bool `config:"use_accelerate_endpoint"`
|
UseAccelerateEndpoint bool `config:"use_accelerate_endpoint"`
|
||||||
LeavePartsOnError bool `config:"leave_parts_on_error"`
|
LeavePartsOnError bool `config:"leave_parts_on_error"`
|
||||||
ListChunk int64 `config:"list_chunk"`
|
ListChunk int64 `config:"list_chunk"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote s3 server
|
// Fs represents a remote s3 server
|
||||||
|
@ -965,7 +971,7 @@ func parsePath(path string) (root string) {
|
||||||
// relative to f.root
|
// relative to f.root
|
||||||
func (f *Fs) split(rootRelativePath string) (bucketName, bucketPath string) {
|
func (f *Fs) split(rootRelativePath string) (bucketName, bucketPath string) {
|
||||||
bucketName, bucketPath = bucket.Split(path.Join(f.root, rootRelativePath))
|
bucketName, bucketPath = bucket.Split(path.Join(f.root, rootRelativePath))
|
||||||
return enc.FromStandardName(bucketName), enc.FromStandardPath(bucketPath)
|
return f.opt.Enc.FromStandardName(bucketName), f.opt.Enc.FromStandardPath(bucketPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// split returns bucket and bucketPath from the object
|
// split returns bucket and bucketPath from the object
|
||||||
|
@ -1166,7 +1172,7 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
|
||||||
}).Fill(f)
|
}).Fill(f)
|
||||||
if f.rootBucket != "" && f.rootDirectory != "" {
|
if f.rootBucket != "" && f.rootDirectory != "" {
|
||||||
// Check to see if the object exists
|
// Check to see if the object exists
|
||||||
encodedDirectory := enc.FromStandardPath(f.rootDirectory)
|
encodedDirectory := f.opt.Enc.FromStandardPath(f.rootDirectory)
|
||||||
req := s3.HeadObjectInput{
|
req := s3.HeadObjectInput{
|
||||||
Bucket: &f.rootBucket,
|
Bucket: &f.rootBucket,
|
||||||
Key: &encodedDirectory,
|
Key: &encodedDirectory,
|
||||||
|
@ -1369,7 +1375,7 @@ func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBuck
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remote = enc.ToStandardPath(remote)
|
remote = f.opt.Enc.ToStandardPath(remote)
|
||||||
if !strings.HasPrefix(remote, prefix) {
|
if !strings.HasPrefix(remote, prefix) {
|
||||||
fs.Logf(f, "Odd name received %q", remote)
|
fs.Logf(f, "Odd name received %q", remote)
|
||||||
continue
|
continue
|
||||||
|
@ -1396,7 +1402,7 @@ func (f *Fs) list(ctx context.Context, bucket, directory, prefix string, addBuck
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remote = enc.ToStandardPath(remote)
|
remote = f.opt.Enc.ToStandardPath(remote)
|
||||||
if !strings.HasPrefix(remote, prefix) {
|
if !strings.HasPrefix(remote, prefix) {
|
||||||
fs.Logf(f, "Odd name received %q", remote)
|
fs.Logf(f, "Odd name received %q", remote)
|
||||||
continue
|
continue
|
||||||
|
@ -1487,7 +1493,7 @@ func (f *Fs) listBuckets(ctx context.Context) (entries fs.DirEntries, err error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, bucket := range resp.Buckets {
|
for _, bucket := range resp.Buckets {
|
||||||
bucketName := enc.ToStandardName(aws.StringValue(bucket.Name))
|
bucketName := f.opt.Enc.ToStandardName(aws.StringValue(bucket.Name))
|
||||||
f.cache.MarkOK(bucketName)
|
f.cache.MarkOK(bucketName)
|
||||||
d := fs.NewDir(bucketName, aws.TimeValue(bucket.CreationDate))
|
d := fs.NewDir(bucketName, aws.TimeValue(bucket.CreationDate))
|
||||||
entries = append(entries, d)
|
entries = append(entries, d)
|
||||||
|
|
|
@ -87,6 +87,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/backend/sharefile/api"
|
"github.com/rclone/rclone/backend/sharefile/api"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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"
|
||||||
|
@ -94,6 +95,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/fserrors"
|
"github.com/rclone/rclone/fs/fserrors"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/random"
|
"github.com/rclone/rclone/lib/random"
|
||||||
|
@ -101,8 +103,6 @@ import (
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.Sharefile
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
rcloneClientID = "djQUPlHTUM9EvayYBWuKC5IrVIoQde46"
|
rcloneClientID = "djQUPlHTUM9EvayYBWuKC5IrVIoQde46"
|
||||||
rcloneEncryptedClientSecret = "v7572bKhUindQL3yDnUAebmgP-QxiwT38JLxVPolcZBl6SSs329MtFzH73x7BeELmMVZtneUPvALSopUZ6VkhQ"
|
rcloneEncryptedClientSecret = "v7572bKhUindQL3yDnUAebmgP-QxiwT38JLxVPolcZBl6SSs329MtFzH73x7BeELmMVZtneUPvALSopUZ6VkhQ"
|
||||||
|
@ -204,6 +204,11 @@ be set manually to something like: https://XXX.sharefile.com
|
||||||
`,
|
`,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
Default: "",
|
Default: "",
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Sharefile,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -214,6 +219,7 @@ type Options struct {
|
||||||
UploadCutoff fs.SizeSuffix `config:"upload_cutoff"`
|
UploadCutoff fs.SizeSuffix `config:"upload_cutoff"`
|
||||||
ChunkSize fs.SizeSuffix `config:"chunk_size"`
|
ChunkSize fs.SizeSuffix `config:"chunk_size"`
|
||||||
Endpoint string `config:"endpoint"`
|
Endpoint string `config:"endpoint"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote cloud storage system
|
// Fs represents a remote cloud storage system
|
||||||
|
@ -301,7 +307,7 @@ func (f *Fs) readMetaDataForIDPath(ctx context.Context, id, path string, directo
|
||||||
}
|
}
|
||||||
if path != "" {
|
if path != "" {
|
||||||
opts.Path += "/ByPath"
|
opts.Path += "/ByPath"
|
||||||
opts.Parameters.Set("path", "/"+enc.FromStandardPath(path))
|
opts.Parameters.Set("path", "/"+f.opt.Enc.FromStandardPath(path))
|
||||||
}
|
}
|
||||||
var item api.Item
|
var item api.Item
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
@ -595,7 +601,7 @@ func (f *Fs) FindLeaf(ctx context.Context, pathID, leaf string) (pathIDOut strin
|
||||||
// CreateDir makes a directory with pathID as parent and name leaf
|
// CreateDir makes a directory with pathID as parent and name leaf
|
||||||
func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, err error) {
|
func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, err error) {
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
leaf = enc.FromStandardName(leaf)
|
leaf = f.opt.Enc.FromStandardName(leaf)
|
||||||
var req = api.Item{
|
var req = api.Item{
|
||||||
Name: leaf,
|
Name: leaf,
|
||||||
FileName: leaf,
|
FileName: leaf,
|
||||||
|
@ -664,7 +670,7 @@ func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, fi
|
||||||
fs.Debugf(f, "Ignoring %q - unknown type %q", item.Name, item.Type)
|
fs.Debugf(f, "Ignoring %q - unknown type %q", item.Name, item.Type)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
item.Name = enc.ToStandardName(item.Name)
|
item.Name = f.opt.Enc.ToStandardName(item.Name)
|
||||||
if fn(item) {
|
if fn(item) {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
|
@ -873,7 +879,7 @@ func (f *Fs) updateItem(ctx context.Context, id, leaf, directoryID string, modTi
|
||||||
"overwrite": {"false"},
|
"overwrite": {"false"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
leaf = enc.FromStandardName(leaf)
|
leaf = f.opt.Enc.FromStandardName(leaf)
|
||||||
// FIXME this appears to be a bug in the API
|
// FIXME this appears to be a bug in the API
|
||||||
//
|
//
|
||||||
// If you set the modified time via PATCH then the server
|
// If you set the modified time via PATCH then the server
|
||||||
|
@ -1119,7 +1125,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (dst fs.Obj
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
srcLeaf = enc.FromStandardName(srcLeaf)
|
srcLeaf = f.opt.Enc.FromStandardName(srcLeaf)
|
||||||
_ = srcParentID
|
_ = srcParentID
|
||||||
|
|
||||||
// Create temporary object
|
// Create temporary object
|
||||||
|
@ -1127,7 +1133,7 @@ func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (dst fs.Obj
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dstLeaf = enc.FromStandardName(dstLeaf)
|
dstLeaf = f.opt.Enc.FromStandardName(dstLeaf)
|
||||||
|
|
||||||
sameName := strings.ToLower(srcLeaf) == strings.ToLower(dstLeaf)
|
sameName := strings.ToLower(srcLeaf) == strings.ToLower(dstLeaf)
|
||||||
if sameName && srcParentID == dstParentID {
|
if sameName && srcParentID == dstParentID {
|
||||||
|
@ -1390,7 +1396,7 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
leaf = enc.FromStandardName(leaf)
|
leaf = o.fs.opt.Enc.FromStandardName(leaf)
|
||||||
var req = api.UploadRequest{
|
var req = api.UploadRequest{
|
||||||
Method: "standard",
|
Method: "standard",
|
||||||
Raw: true,
|
Raw: true,
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/ncw/swift"
|
"github.com/ncw/swift"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"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/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
|
@ -25,6 +26,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/operations"
|
"github.com/rclone/rclone/fs/operations"
|
||||||
"github.com/rclone/rclone/fs/walk"
|
"github.com/rclone/rclone/fs/walk"
|
||||||
"github.com/rclone/rclone/lib/bucket"
|
"github.com/rclone/rclone/lib/bucket"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/readers"
|
"github.com/rclone/rclone/lib/readers"
|
||||||
)
|
)
|
||||||
|
@ -60,10 +62,13 @@ Rclone will still chunk files bigger than chunk_size when doing normal
|
||||||
copy operations.`,
|
copy operations.`,
|
||||||
Default: false,
|
Default: false,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Swift,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
const enc = encodings.Swift
|
|
||||||
|
|
||||||
// Register with Fs
|
// Register with Fs
|
||||||
func init() {
|
func init() {
|
||||||
fs.Register(&fs.RegInfo{
|
fs.Register(&fs.RegInfo{
|
||||||
|
@ -207,6 +212,7 @@ type Options struct {
|
||||||
EndpointType string `config:"endpoint_type"`
|
EndpointType string `config:"endpoint_type"`
|
||||||
ChunkSize fs.SizeSuffix `config:"chunk_size"`
|
ChunkSize fs.SizeSuffix `config:"chunk_size"`
|
||||||
NoChunk bool `config:"no_chunk"`
|
NoChunk bool `config:"no_chunk"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote swift server
|
// Fs represents a remote swift server
|
||||||
|
@ -325,7 +331,7 @@ func parsePath(path string) (root string) {
|
||||||
// relative to f.root
|
// relative to f.root
|
||||||
func (f *Fs) split(rootRelativePath string) (container, containerPath string) {
|
func (f *Fs) split(rootRelativePath string) (container, containerPath string) {
|
||||||
container, containerPath = bucket.Split(path.Join(f.root, rootRelativePath))
|
container, containerPath = bucket.Split(path.Join(f.root, rootRelativePath))
|
||||||
return enc.FromStandardName(container), enc.FromStandardPath(containerPath)
|
return f.opt.Enc.FromStandardName(container), f.opt.Enc.FromStandardPath(containerPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// split returns container and containerPath from the object
|
// split returns container and containerPath from the object
|
||||||
|
@ -446,7 +452,7 @@ func NewFsWithConnection(opt *Options, name, root string, c *swift.Connection, n
|
||||||
// Check to see if the object exists - ignoring directory markers
|
// Check to see if the object exists - ignoring directory markers
|
||||||
var info swift.Object
|
var info swift.Object
|
||||||
var err error
|
var err error
|
||||||
encodedDirectory := enc.FromStandardPath(f.rootDirectory)
|
encodedDirectory := f.opt.Enc.FromStandardPath(f.rootDirectory)
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
var rxHeaders swift.Headers
|
var rxHeaders swift.Headers
|
||||||
info, rxHeaders, err = f.c.Object(f.rootContainer, encodedDirectory)
|
info, rxHeaders, err = f.c.Object(f.rootContainer, encodedDirectory)
|
||||||
|
@ -559,7 +565,7 @@ func (f *Fs) listContainerRoot(container, directory, prefix string, addContainer
|
||||||
if !recurse {
|
if !recurse {
|
||||||
isDirectory = strings.HasSuffix(object.Name, "/")
|
isDirectory = strings.HasSuffix(object.Name, "/")
|
||||||
}
|
}
|
||||||
remote := enc.ToStandardPath(object.Name)
|
remote := f.opt.Enc.ToStandardPath(object.Name)
|
||||||
if !strings.HasPrefix(remote, prefix) {
|
if !strings.HasPrefix(remote, prefix) {
|
||||||
fs.Logf(f, "Odd name received %q", remote)
|
fs.Logf(f, "Odd name received %q", remote)
|
||||||
continue
|
continue
|
||||||
|
@ -642,7 +648,7 @@ func (f *Fs) listContainers(ctx context.Context) (entries fs.DirEntries, err err
|
||||||
}
|
}
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
f.cache.MarkOK(container.Name)
|
f.cache.MarkOK(container.Name)
|
||||||
d := fs.NewDir(enc.ToStandardName(container.Name), time.Time{}).SetSize(container.Bytes).SetItems(container.Count)
|
d := fs.NewDir(f.opt.Enc.ToStandardName(container.Name), time.Time{}).SetSize(container.Bytes).SetItems(container.Count)
|
||||||
entries = append(entries, d)
|
entries = append(entries, d)
|
||||||
}
|
}
|
||||||
return entries, nil
|
return entries, nil
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/encodings"
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
"github.com/rclone/rclone/fs/fserrors"
|
"github.com/rclone/rclone/fs/fserrors"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
|
"github.com/rclone/rclone/lib/encoder"
|
||||||
"github.com/rclone/rclone/lib/oauthutil"
|
"github.com/rclone/rclone/lib/oauthutil"
|
||||||
"github.com/rclone/rclone/lib/pacer"
|
"github.com/rclone/rclone/lib/pacer"
|
||||||
"github.com/rclone/rclone/lib/readers"
|
"github.com/rclone/rclone/lib/readers"
|
||||||
|
@ -30,8 +31,6 @@ import (
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const enc = encodings.Yandex
|
|
||||||
|
|
||||||
//oAuth
|
//oAuth
|
||||||
const (
|
const (
|
||||||
rcloneClientID = "ac39b43b9eba4cae8ffb788c06d816a8"
|
rcloneClientID = "ac39b43b9eba4cae8ffb788c06d816a8"
|
||||||
|
@ -80,6 +79,11 @@ func init() {
|
||||||
Help: "Remove existing public link to file/folder with link command rather than creating.\nDefault is false, meaning link command will create or retrieve public link.",
|
Help: "Remove existing public link to file/folder with link command rather than creating.\nDefault is false, meaning link command will create or retrieve public link.",
|
||||||
Default: false,
|
Default: false,
|
||||||
Advanced: true,
|
Advanced: true,
|
||||||
|
}, {
|
||||||
|
Name: config.ConfigEncoding,
|
||||||
|
Help: config.ConfigEncodingHelp,
|
||||||
|
Advanced: true,
|
||||||
|
Default: encodings.Yandex,
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -88,6 +92,7 @@ func init() {
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Token string `config:"token"`
|
Token string `config:"token"`
|
||||||
Unlink bool `config:"unlink"`
|
Unlink bool `config:"unlink"`
|
||||||
|
Enc encoder.MultiEncoder `config:"encoding"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fs represents a remote yandex
|
// Fs represents a remote yandex
|
||||||
|
@ -210,7 +215,7 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string, options *api.
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Parameters.Set("path", enc.FromStandardPath(path))
|
opts.Parameters.Set("path", f.opt.Enc.FromStandardPath(path))
|
||||||
|
|
||||||
if options.SortMode != nil {
|
if options.SortMode != nil {
|
||||||
opts.Parameters.Set("sort", options.SortMode.String())
|
opts.Parameters.Set("sort", options.SortMode.String())
|
||||||
|
@ -237,7 +242,7 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string, options *api.
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info.Name = enc.ToStandardName(info.Name)
|
info.Name = f.opt.Enc.ToStandardName(info.Name)
|
||||||
return &info, nil
|
return &info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +369,7 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e
|
||||||
if info.ResourceType == "dir" {
|
if info.ResourceType == "dir" {
|
||||||
//list all subdirs
|
//list all subdirs
|
||||||
for _, element := range info.Embedded.Items {
|
for _, element := range info.Embedded.Items {
|
||||||
element.Name = enc.ToStandardName(element.Name)
|
element.Name = f.opt.Enc.ToStandardName(element.Name)
|
||||||
remote := path.Join(dir, element.Name)
|
remote := path.Join(dir, element.Name)
|
||||||
entry, err := f.itemToDirEntry(ctx, remote, &element)
|
entry, err := f.itemToDirEntry(ctx, remote, &element)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -467,7 +472,7 @@ func (f *Fs) CreateDir(ctx context.Context, path string) (err error) {
|
||||||
if strings.IndexRune(path, ':') >= 0 {
|
if strings.IndexRune(path, ':') >= 0 {
|
||||||
path = "disk:" + path
|
path = "disk:" + path
|
||||||
}
|
}
|
||||||
opts.Parameters.Set("path", enc.FromStandardPath(path))
|
opts.Parameters.Set("path", f.opt.Enc.FromStandardPath(path))
|
||||||
|
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
resp, err = f.srv.Call(ctx, &opts)
|
resp, err = f.srv.Call(ctx, &opts)
|
||||||
|
@ -581,7 +586,7 @@ func (f *Fs) delete(ctx context.Context, path string, hardDelete bool) (err erro
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Parameters.Set("path", enc.FromStandardPath(path))
|
opts.Parameters.Set("path", f.opt.Enc.FromStandardPath(path))
|
||||||
opts.Parameters.Set("permanently", strconv.FormatBool(hardDelete))
|
opts.Parameters.Set("permanently", strconv.FormatBool(hardDelete))
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
@ -653,8 +658,8 @@ func (f *Fs) copyOrMove(ctx context.Context, method, src, dst string, overwrite
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Parameters.Set("from", enc.FromStandardPath(src))
|
opts.Parameters.Set("from", f.opt.Enc.FromStandardPath(src))
|
||||||
opts.Parameters.Set("path", enc.FromStandardPath(dst))
|
opts.Parameters.Set("path", f.opt.Enc.FromStandardPath(dst))
|
||||||
opts.Parameters.Set("overwrite", strconv.FormatBool(overwrite))
|
opts.Parameters.Set("overwrite", strconv.FormatBool(overwrite))
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
@ -803,12 +808,12 @@ func (f *Fs) PublicLink(ctx context.Context, remote string) (link string, err er
|
||||||
}
|
}
|
||||||
opts := rest.Opts{
|
opts := rest.Opts{
|
||||||
Method: "PUT",
|
Method: "PUT",
|
||||||
Path: enc.FromStandardPath(path),
|
Path: f.opt.Enc.FromStandardPath(path),
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
NoResponse: true,
|
NoResponse: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Parameters.Set("path", enc.FromStandardPath(f.filePath(remote)))
|
opts.Parameters.Set("path", f.opt.Enc.FromStandardPath(f.filePath(remote)))
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
err = f.pacer.Call(func() (bool, error) {
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
@ -994,7 +999,7 @@ func (o *Object) setCustomProperty(ctx context.Context, property string, value s
|
||||||
NoResponse: true,
|
NoResponse: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Parameters.Set("path", enc.FromStandardPath(o.filePath()))
|
opts.Parameters.Set("path", o.fs.opt.Enc.FromStandardPath(o.filePath()))
|
||||||
rcm := map[string]interface{}{
|
rcm := map[string]interface{}{
|
||||||
property: value,
|
property: value,
|
||||||
}
|
}
|
||||||
|
@ -1031,7 +1036,7 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.Read
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Parameters.Set("path", enc.FromStandardPath(o.filePath()))
|
opts.Parameters.Set("path", o.fs.opt.Enc.FromStandardPath(o.filePath()))
|
||||||
|
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
resp, err = o.fs.srv.CallJSON(ctx, &opts, nil, &dl)
|
resp, err = o.fs.srv.CallJSON(ctx, &opts, nil, &dl)
|
||||||
|
@ -1068,7 +1073,7 @@ func (o *Object) upload(ctx context.Context, in io.Reader, overwrite bool, mimeT
|
||||||
Parameters: url.Values{},
|
Parameters: url.Values{},
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Parameters.Set("path", enc.FromStandardPath(o.filePath()))
|
opts.Parameters.Set("path", o.fs.opt.Enc.FromStandardPath(o.filePath()))
|
||||||
opts.Parameters.Set("overwrite", strconv.FormatBool(overwrite))
|
opts.Parameters.Set("overwrite", strconv.FormatBool(overwrite))
|
||||||
|
|
||||||
err = o.fs.pacer.Call(func() (bool, error) {
|
err = o.fs.pacer.Call(func() (bool, error) {
|
||||||
|
|
|
@ -60,6 +60,12 @@ const (
|
||||||
// ConfigTokenURL is the config key used to store the token server endpoint
|
// ConfigTokenURL is the config key used to store the token server endpoint
|
||||||
ConfigTokenURL = "token_url"
|
ConfigTokenURL = "token_url"
|
||||||
|
|
||||||
|
// ConfigEncoding is the config key to change the encoding for a backend
|
||||||
|
ConfigEncoding = "encoding"
|
||||||
|
|
||||||
|
// ConfigEncodingHelp is the help for ConfigEncoding
|
||||||
|
ConfigEncodingHelp = "This sets the encoding for the backend.\n\nSee: the [encoding section in the overview](/overview/#encoding) for more info."
|
||||||
|
|
||||||
// ConfigAuthorize indicates that we just want "rclone authorize"
|
// ConfigAuthorize indicates that we just want "rclone authorize"
|
||||||
ConfigAuthorize = "config_authorize"
|
ConfigAuthorize = "config_authorize"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package config
|
package config_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
_ "github.com/rclone/rclone/backend/local"
|
_ "github.com/rclone/rclone/backend/local"
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
|
"github.com/rclone/rclone/fs/config"
|
||||||
"github.com/rclone/rclone/fs/config/obscure"
|
"github.com/rclone/rclone/fs/config/obscure"
|
||||||
"github.com/rclone/rclone/fs/rc"
|
"github.com/rclone/rclone/fs/rc"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -28,8 +29,8 @@ func TestRc(t *testing.T) {
|
||||||
out, err := call.Fn(context.Background(), in)
|
out, err := call.Fn(context.Background(), in)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Nil(t, out)
|
require.Nil(t, out)
|
||||||
assert.Equal(t, "local", FileGet(testName, "type"))
|
assert.Equal(t, "local", config.FileGet(testName, "type"))
|
||||||
assert.Equal(t, "sausage", FileGet(testName, "test_key"))
|
assert.Equal(t, "sausage", config.FileGet(testName, "test_key"))
|
||||||
|
|
||||||
// The sub tests rely on the remote created above but they can
|
// The sub tests rely on the remote created above but they can
|
||||||
// all be run independently
|
// all be run independently
|
||||||
|
@ -92,9 +93,9 @@ func TestRc(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Nil(t, out)
|
assert.Nil(t, out)
|
||||||
|
|
||||||
assert.Equal(t, "local", FileGet(testName, "type"))
|
assert.Equal(t, "local", config.FileGet(testName, "type"))
|
||||||
assert.Equal(t, "rutabaga", FileGet(testName, "test_key"))
|
assert.Equal(t, "rutabaga", config.FileGet(testName, "test_key"))
|
||||||
assert.Equal(t, "cabbage", FileGet(testName, "test_key2"))
|
assert.Equal(t, "cabbage", config.FileGet(testName, "test_key2"))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Password", func(t *testing.T) {
|
t.Run("Password", func(t *testing.T) {
|
||||||
|
@ -111,9 +112,9 @@ func TestRc(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Nil(t, out)
|
assert.Nil(t, out)
|
||||||
|
|
||||||
assert.Equal(t, "local", FileGet(testName, "type"))
|
assert.Equal(t, "local", config.FileGet(testName, "type"))
|
||||||
assert.Equal(t, "rutabaga", obscure.MustReveal(FileGet(testName, "test_key")))
|
assert.Equal(t, "rutabaga", obscure.MustReveal(config.FileGet(testName, "test_key")))
|
||||||
assert.Equal(t, "cabbage", obscure.MustReveal(FileGet(testName, "test_key2")))
|
assert.Equal(t, "cabbage", obscure.MustReveal(config.FileGet(testName, "test_key2")))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Delete the test remote
|
// Delete the test remote
|
||||||
|
@ -125,8 +126,8 @@ func TestRc(t *testing.T) {
|
||||||
out, err = call.Fn(context.Background(), in)
|
out, err = call.Fn(context.Background(), in)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Nil(t, out)
|
assert.Nil(t, out)
|
||||||
assert.Equal(t, "", FileGet(testName, "type"))
|
assert.Equal(t, "", config.FileGet(testName, "type"))
|
||||||
assert.Equal(t, "", FileGet(testName, "test_key"))
|
assert.Equal(t, "", config.FileGet(testName, "test_key"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRcProviders(t *testing.T) {
|
func TestRcProviders(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue