forked from TrueCloudLab/rclone
sharefile: use lib/encoder
This commit is contained in:
parent
5cef5f8b49
commit
b581f2de26
4 changed files with 25 additions and 114 deletions
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
Translate file names for sharefile
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sharefile
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// charMap holds replacements for characters
|
|
||||||
//
|
|
||||||
// Sharefile has a restricted set of characters compared to other
|
|
||||||
// cloud storage systems, so we to map these to the FULLWIDTH unicode
|
|
||||||
// equivalents
|
|
||||||
//
|
|
||||||
// http://unicode-search.net/unicode-namesearch.pl?term=SOLIDUS
|
|
||||||
var (
|
|
||||||
charMap = map[rune]rune{
|
|
||||||
'\\': '\', // FULLWIDTH REVERSE SOLIDUS
|
|
||||||
'*': '*', // FULLWIDTH ASTERISK
|
|
||||||
'<': '<', // FULLWIDTH LESS-THAN SIGN
|
|
||||||
'>': '>', // FULLWIDTH GREATER-THAN SIGN
|
|
||||||
'?': '?', // FULLWIDTH QUESTION MARK
|
|
||||||
':': ':', // FULLWIDTH COLON
|
|
||||||
'|': '|', // FULLWIDTH VERTICAL LINE
|
|
||||||
'"': '"', // FULLWIDTH QUOTATION MARK
|
|
||||||
'.': '.', // FULLWIDTH FULL STOP
|
|
||||||
' ': '␠', // SYMBOL FOR SPACE
|
|
||||||
}
|
|
||||||
invCharMap map[rune]rune
|
|
||||||
fixStartingWithPeriod = regexp.MustCompile(`(/|^)\.`)
|
|
||||||
fixEndingWithPeriod = regexp.MustCompile(`\.(/|$)`)
|
|
||||||
fixStartingWithSpace = regexp.MustCompile(`(/|^) `)
|
|
||||||
fixEndingWithSpace = regexp.MustCompile(` (/|$)`)
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Create inverse charMap
|
|
||||||
invCharMap = make(map[rune]rune, len(charMap))
|
|
||||||
for k, v := range charMap {
|
|
||||||
invCharMap[v] = k
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// replaceReservedChars takes a path and substitutes any reserved
|
|
||||||
// characters in it
|
|
||||||
func replaceReservedChars(in string) string {
|
|
||||||
// Names can't start with a period '.'
|
|
||||||
in = fixStartingWithPeriod.ReplaceAllString(in, "$1"+string(charMap['.']))
|
|
||||||
// Names can't end with a period '.'
|
|
||||||
in = fixEndingWithPeriod.ReplaceAllString(in, string(charMap['.'])+"$1")
|
|
||||||
// Names can't start with space
|
|
||||||
in = fixStartingWithSpace.ReplaceAllString(in, "$1"+string(charMap[' ']))
|
|
||||||
// Names can't end with space
|
|
||||||
in = fixEndingWithSpace.ReplaceAllString(in, string(charMap[' '])+"$1")
|
|
||||||
// Replace reserved characters
|
|
||||||
return strings.Map(func(c rune) rune {
|
|
||||||
if replacement, ok := charMap[c]; ok && c != '.' && c != '~' && c != ' ' {
|
|
||||||
return replacement
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}, in)
|
|
||||||
}
|
|
||||||
|
|
||||||
// restoreReservedChars takes a path and undoes any substitutions
|
|
||||||
// made by replaceReservedChars
|
|
||||||
func restoreReservedChars(in string) string {
|
|
||||||
return strings.Map(func(c rune) rune {
|
|
||||||
if replacement, ok := invCharMap[c]; ok {
|
|
||||||
return replacement
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}, in)
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package sharefile
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestReplace(t *testing.T) {
|
|
||||||
for _, test := range []struct {
|
|
||||||
in string
|
|
||||||
out string
|
|
||||||
}{
|
|
||||||
{"", ""},
|
|
||||||
{"abc 123", "abc 123"},
|
|
||||||
{`\*<>?:|#%".~`, `\*<>?:|#%".~`},
|
|
||||||
{`\*<>?:|#%".~/\*<>?:|#%".~`, `\*<>?:|#%".~/\*<>?:|#%".~`},
|
|
||||||
{" leading space", "␠leading space"},
|
|
||||||
{"trailing space ", "trailing space␠"},
|
|
||||||
{".leading dot", ".leading dot"},
|
|
||||||
{"trailing dot.", "trailing dot."},
|
|
||||||
{" leading space/ leading space/ leading space", "␠leading space/␠leading space/␠leading space"},
|
|
||||||
{"trailing dot./trailing dot./trailing dot.", "trailing dot./trailing dot./trailing dot."},
|
|
||||||
{".leading dot/..leading dot/.leading dot", ".leading dot/..leading dot/.leading dot"},
|
|
||||||
} {
|
|
||||||
got := replaceReservedChars(test.in)
|
|
||||||
if got != test.out {
|
|
||||||
t.Errorf("replaceReservedChars(%q) want %q got %q", test.in, test.out, got)
|
|
||||||
}
|
|
||||||
got2 := restoreReservedChars(got)
|
|
||||||
if got2 != test.in {
|
|
||||||
t.Errorf("restoreReservedChars(%q) want %q got %q", got, test.in, got2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -90,6 +90,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/config/configmap"
|
"github.com/rclone/rclone/fs/config/configmap"
|
||||||
"github.com/rclone/rclone/fs/config/configstruct"
|
"github.com/rclone/rclone/fs/config/configstruct"
|
||||||
"github.com/rclone/rclone/fs/config/obscure"
|
"github.com/rclone/rclone/fs/config/obscure"
|
||||||
|
"github.com/rclone/rclone/fs/encodings"
|
||||||
"github.com/rclone/rclone/fs/fserrors"
|
"github.com/rclone/rclone/fs/fserrors"
|
||||||
"github.com/rclone/rclone/fs/hash"
|
"github.com/rclone/rclone/fs/hash"
|
||||||
"github.com/rclone/rclone/lib/dircache"
|
"github.com/rclone/rclone/lib/dircache"
|
||||||
|
@ -100,6 +101,8 @@ 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"
|
||||||
|
@ -298,7 +301,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", "/"+replaceReservedChars(path))
|
opts.Parameters.Set("path", "/"+enc.FromStandardPath(path))
|
||||||
}
|
}
|
||||||
var item api.Item
|
var item api.Item
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
@ -592,7 +595,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 = replaceReservedChars(leaf)
|
leaf = enc.FromStandardName(leaf)
|
||||||
var req = api.Item{
|
var req = api.Item{
|
||||||
Name: leaf,
|
Name: leaf,
|
||||||
FileName: leaf,
|
FileName: leaf,
|
||||||
|
@ -661,7 +664,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 = restoreReservedChars(item.Name)
|
item.Name = enc.ToStandardName(item.Name)
|
||||||
if fn(item) {
|
if fn(item) {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
|
@ -870,7 +873,7 @@ func (f *Fs) updateItem(ctx context.Context, id, leaf, directoryID string, modTi
|
||||||
"overwrite": {"false"},
|
"overwrite": {"false"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
leaf = replaceReservedChars(leaf)
|
leaf = 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
|
||||||
|
@ -1116,7 +1119,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 = replaceReservedChars(srcLeaf)
|
srcLeaf = enc.FromStandardName(srcLeaf)
|
||||||
_ = srcParentID
|
_ = srcParentID
|
||||||
|
|
||||||
// Create temporary object
|
// Create temporary object
|
||||||
|
@ -1124,7 +1127,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 = replaceReservedChars(dstLeaf)
|
dstLeaf = 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 {
|
||||||
|
@ -1387,7 +1390,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 = replaceReservedChars(leaf)
|
leaf = enc.FromStandardName(leaf)
|
||||||
var req = api.UploadRequest{
|
var req = api.UploadRequest{
|
||||||
Method: "standard",
|
Method: "standard",
|
||||||
Raw: true,
|
Raw: true,
|
||||||
|
|
|
@ -302,6 +302,18 @@ const QingStor = encoder.MultiEncoder(
|
||||||
encoder.EncodeCtl |
|
encoder.EncodeCtl |
|
||||||
encoder.EncodeSlash)
|
encoder.EncodeSlash)
|
||||||
|
|
||||||
|
// Sharefile is the encoding used by the sharefile backend
|
||||||
|
const Sharefile = encoder.MultiEncoder(
|
||||||
|
uint(Base) |
|
||||||
|
encoder.EncodeWin | // :?"*<>|
|
||||||
|
encoder.EncodeBackSlash | // \
|
||||||
|
encoder.EncodeCtl |
|
||||||
|
encoder.EncodeRightSpace |
|
||||||
|
encoder.EncodeRightPeriod |
|
||||||
|
encoder.EncodeLeftSpace |
|
||||||
|
encoder.EncodeLeftPeriod |
|
||||||
|
encoder.EncodeInvalidUtf8)
|
||||||
|
|
||||||
// ByName returns the encoder for a give backend name or nil
|
// ByName returns the encoder for a give backend name or nil
|
||||||
func ByName(name string) encoder.Encoder {
|
func ByName(name string) encoder.Encoder {
|
||||||
switch strings.ToLower(name) {
|
switch strings.ToLower(name) {
|
||||||
|
@ -353,6 +365,8 @@ func ByName(name string) encoder.Encoder {
|
||||||
return QingStor
|
return QingStor
|
||||||
case "s3":
|
case "s3":
|
||||||
return S3
|
return S3
|
||||||
|
case "sharefile":
|
||||||
|
return Sharefile
|
||||||
//case "sftp":
|
//case "sftp":
|
||||||
case "swift":
|
case "swift":
|
||||||
return Swift
|
return Swift
|
||||||
|
@ -392,5 +406,6 @@ func Names() []string {
|
||||||
"onedrive",
|
"onedrive",
|
||||||
"opendrive",
|
"opendrive",
|
||||||
"pcloud",
|
"pcloud",
|
||||||
|
"sharefile",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue