package filename import ( "bytes" "encoding/base64" "encoding/binary" "errors" "sync" "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 scsuDecode(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 scsuDecode(name) } return string(name), nil } }