github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/lib/encoder/filename/decode.go (about)

     1  package filename
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"encoding/binary"
     7  	"errors"
     8  	"sync"
     9  
    10  	"github.com/dop251/scsu"
    11  	"github.com/klauspost/compress/huff0"
    12  )
    13  
    14  // ErrCorrupted is returned if a provided encoded filename cannot be decoded.
    15  var ErrCorrupted = errors.New("file name corrupt")
    16  
    17  // ErrUnsupported is returned if a provided encoding may come from a future version or the file name is corrupt.
    18  var ErrUnsupported = errors.New("file name possibly generated by future version of rclone")
    19  
    20  // Custom decoder for tableCustom types. Stateful, so must have lock.
    21  var customDec huff0.Scratch
    22  var customDecMu sync.Mutex
    23  
    24  // Decode an encoded string.
    25  func Decode(s string) (string, error) {
    26  	initCoders()
    27  	if len(s) < 1 {
    28  		return "", ErrCorrupted
    29  	}
    30  	table := decodeMap[s[0]]
    31  	if table == 0 {
    32  		return "", ErrCorrupted
    33  	}
    34  	table--
    35  	s = s[1:]
    36  	data := make([]byte, base64.URLEncoding.DecodedLen(len(s)))
    37  	n, err := base64.URLEncoding.Decode(data, ([]byte)(s))
    38  	if err != nil || n < 0 {
    39  		return "", ErrCorrupted
    40  	}
    41  	data = data[:n]
    42  	return DecodeBytes(table, data)
    43  }
    44  
    45  // DecodeBytes will decode raw id and data values.
    46  func DecodeBytes(table byte, data []byte) (string, error) {
    47  	initCoders()
    48  	switch table {
    49  	case tableUncompressed:
    50  		return string(data), nil
    51  	case tableReserved:
    52  		return "", ErrUnsupported
    53  	case tableSCSUPlain:
    54  		return scsu.Decode(data)
    55  	case tableRLE:
    56  		if len(data) < 2 {
    57  			return "", ErrCorrupted
    58  		}
    59  		n, used := binary.Uvarint(data[:len(data)-1])
    60  		if used <= 0 || n > maxLength {
    61  			return "", ErrCorrupted
    62  		}
    63  		return string(bytes.Repeat(data[len(data)-1:], int(n))), nil
    64  	case tableCustom:
    65  		customDecMu.Lock()
    66  		defer customDecMu.Unlock()
    67  		_, data, err := huff0.ReadTable(data, &customDec)
    68  		if err != nil {
    69  			return "", ErrCorrupted
    70  		}
    71  		customDec.MaxDecodedSize = maxLength
    72  		decoded, err := customDec.Decompress1X(data)
    73  		if err != nil {
    74  			return "", ErrCorrupted
    75  		}
    76  		return string(decoded), nil
    77  	default:
    78  		if table >= byte(len(decTables)) {
    79  			return "", ErrCorrupted
    80  		}
    81  		dec := decTables[table]
    82  		if dec == nil {
    83  			return "", ErrUnsupported
    84  		}
    85  		var dst [maxLength]byte
    86  		name, err := dec.Decompress1X(dst[:0], data)
    87  		if err != nil {
    88  			return "", ErrCorrupted
    89  		}
    90  		if table == tableSCSU {
    91  			return scsu.Decode(name)
    92  		}
    93  		return string(name), nil
    94  	}
    95  }