sharefile: use lib/encoder

This commit is contained in:
Nick Craig-Wood 2019-09-30 16:41:18 +01:00
parent 5cef5f8b49
commit b581f2de26
4 changed files with 25 additions and 114 deletions

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -90,6 +90,7 @@ import (
"github.com/rclone/rclone/fs/config/configmap"
"github.com/rclone/rclone/fs/config/configstruct"
"github.com/rclone/rclone/fs/config/obscure"
"github.com/rclone/rclone/fs/encodings"
"github.com/rclone/rclone/fs/fserrors"
"github.com/rclone/rclone/fs/hash"
"github.com/rclone/rclone/lib/dircache"
@ -100,6 +101,8 @@ import (
"golang.org/x/oauth2"
)
const enc = encodings.Sharefile
const (
rcloneClientID = "djQUPlHTUM9EvayYBWuKC5IrVIoQde46"
rcloneEncryptedClientSecret = "v7572bKhUindQL3yDnUAebmgP-QxiwT38JLxVPolcZBl6SSs329MtFzH73x7BeELmMVZtneUPvALSopUZ6VkhQ"
@ -298,7 +301,7 @@ func (f *Fs) readMetaDataForIDPath(ctx context.Context, id, path string, directo
}
if path != "" {
opts.Path += "/ByPath"
opts.Parameters.Set("path", "/"+replaceReservedChars(path))
opts.Parameters.Set("path", "/"+enc.FromStandardPath(path))
}
var item api.Item
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
func (f *Fs) CreateDir(ctx context.Context, pathID, leaf string) (newID string, err error) {
var resp *http.Response
leaf = replaceReservedChars(leaf)
leaf = enc.FromStandardName(leaf)
var req = api.Item{
Name: 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)
continue
}
item.Name = restoreReservedChars(item.Name)
item.Name = enc.ToStandardName(item.Name)
if fn(item) {
found = true
break
@ -870,7 +873,7 @@ func (f *Fs) updateItem(ctx context.Context, id, leaf, directoryID string, modTi
"overwrite": {"false"},
},
}
leaf = replaceReservedChars(leaf)
leaf = enc.FromStandardName(leaf)
// FIXME this appears to be a bug in the API
//
// 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 {
return nil, err
}
srcLeaf = replaceReservedChars(srcLeaf)
srcLeaf = enc.FromStandardName(srcLeaf)
_ = srcParentID
// 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 {
return nil, err
}
dstLeaf = replaceReservedChars(dstLeaf)
dstLeaf = enc.FromStandardName(dstLeaf)
sameName := strings.ToLower(srcLeaf) == strings.ToLower(dstLeaf)
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 {
return err
}
leaf = replaceReservedChars(leaf)
leaf = enc.FromStandardName(leaf)
var req = api.UploadRequest{
Method: "standard",
Raw: true,

View File

@ -302,6 +302,18 @@ const QingStor = encoder.MultiEncoder(
encoder.EncodeCtl |
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
func ByName(name string) encoder.Encoder {
switch strings.ToLower(name) {
@ -353,6 +365,8 @@ func ByName(name string) encoder.Encoder {
return QingStor
case "s3":
return S3
case "sharefile":
return Sharefile
//case "sftp":
case "swift":
return Swift
@ -392,5 +406,6 @@ func Names() []string {
"onedrive",
"opendrive",
"pcloud",
"sharefile",
}
}