mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 22:22:21 +08:00
424aaac2e1
Instead of only adding SCSU, add it as an existing table. Allow direct SCSU and add a, perhaps, reasonable table as well. Add byte interfaces that doesn't base64 encode the URL as well with `EncodeBytes` and `DecodeBytes`. Fuzz tested and decode tests added.
96 lines
2.3 KiB
Go
96 lines
2.3 KiB
Go
package filename
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"errors"
|
|
"sync"
|
|
|
|
"github.com/dop251/scsu"
|
|
"github.com/klauspost/compress/huff0"
|
|
)
|
|
|
|
// ErrCorrupted is returned if a provided encoded filename cannot be decoded.
|
|
var ErrCorrupted = errors.New("file name corrupt")
|
|
|
|
// ErrUnsupported is returned if a provided encoding may come from a future version or the file name is corrupt.
|
|
var ErrUnsupported = errors.New("file name possibly generated by future version of rclone")
|
|
|
|
// Custom decoder for tableCustom types. Stateful, so must have lock.
|
|
var customDec huff0.Scratch
|
|
var customDecMu sync.Mutex
|
|
|
|
// Decode an encoded string.
|
|
func Decode(s string) (string, error) {
|
|
initCoders()
|
|
if len(s) < 1 {
|
|
return "", ErrCorrupted
|
|
}
|
|
table := decodeMap[s[0]]
|
|
if table == 0 {
|
|
return "", ErrCorrupted
|
|
}
|
|
table--
|
|
s = s[1:]
|
|
data := make([]byte, base64.URLEncoding.DecodedLen(len(s)))
|
|
n, err := base64.URLEncoding.Decode(data, ([]byte)(s))
|
|
if err != nil || n < 0 {
|
|
return "", ErrCorrupted
|
|
}
|
|
data = data[:n]
|
|
return DecodeBytes(table, data)
|
|
}
|
|
|
|
// DecodeBytes will decode raw id and data values.
|
|
func DecodeBytes(table byte, data []byte) (string, error) {
|
|
initCoders()
|
|
switch table {
|
|
case tableUncompressed:
|
|
return string(data), nil
|
|
case tableReserved:
|
|
return "", ErrUnsupported
|
|
case tableSCSUPlain:
|
|
return scsu.Decode(data)
|
|
case tableRLE:
|
|
if len(data) < 2 {
|
|
return "", ErrCorrupted
|
|
}
|
|
n, used := binary.Uvarint(data[:len(data)-1])
|
|
if used <= 0 || n > maxLength {
|
|
return "", ErrCorrupted
|
|
}
|
|
return string(bytes.Repeat(data[len(data)-1:], int(n))), nil
|
|
case tableCustom:
|
|
customDecMu.Lock()
|
|
defer customDecMu.Unlock()
|
|
_, data, err := huff0.ReadTable(data, &customDec)
|
|
if err != nil {
|
|
return "", ErrCorrupted
|
|
}
|
|
customDec.MaxDecodedSize = maxLength
|
|
decoded, err := customDec.Decompress1X(data)
|
|
if err != nil {
|
|
return "", ErrCorrupted
|
|
}
|
|
return string(decoded), nil
|
|
default:
|
|
if table >= byte(len(decTables)) {
|
|
return "", ErrCorrupted
|
|
}
|
|
dec := decTables[table]
|
|
if dec == nil {
|
|
return "", ErrUnsupported
|
|
}
|
|
var dst [maxLength]byte
|
|
name, err := dec.Decompress1X(dst[:0], data)
|
|
if err != nil {
|
|
return "", ErrCorrupted
|
|
}
|
|
if table == tableSCSU {
|
|
return scsu.Decode(name)
|
|
}
|
|
return string(name), nil
|
|
}
|
|
}
|