github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/gopkg.in/alexcesaro/quotedprintable.v3/encodedword.go (about)

     1  package quotedprintable
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"strings"
    10  	"unicode"
    11  	"unicode/utf8"
    12  )
    13  
    14  // A WordEncoder is a RFC 2047 encoded-word encoder.
    15  type WordEncoder byte
    16  
    17  const (
    18  	// BEncoding represents Base64 encoding scheme as defined by RFC 2045.
    19  	BEncoding = WordEncoder('b')
    20  	// QEncoding represents the Q-encoding scheme as defined by RFC 2047.
    21  	QEncoding = WordEncoder('q')
    22  )
    23  
    24  var (
    25  	errInvalidWord = errors.New("mime: invalid RFC 2047 encoded-word")
    26  )
    27  
    28  // Encode returns the encoded-word form of s. If s is ASCII without special
    29  // characters, it is returned unchanged. The provided charset is the IANA
    30  // charset name of s. It is case insensitive.
    31  func (e WordEncoder) Encode(charset, s string) string {
    32  	if !needsEncoding(s) {
    33  		return s
    34  	}
    35  	return e.encodeWord(charset, s)
    36  }
    37  
    38  func needsEncoding(s string) bool {
    39  	for _, b := range s {
    40  		if (b < ' ' || b > '~') && b != '\t' {
    41  			return true
    42  		}
    43  	}
    44  	return false
    45  }
    46  
    47  // encodeWord encodes a string into an encoded-word.
    48  func (e WordEncoder) encodeWord(charset, s string) string {
    49  	buf := getBuffer()
    50  	defer putBuffer(buf)
    51  
    52  	buf.WriteString("=?")
    53  	buf.WriteString(charset)
    54  	buf.WriteByte('?')
    55  	buf.WriteByte(byte(e))
    56  	buf.WriteByte('?')
    57  
    58  	if e == BEncoding {
    59  		w := base64.NewEncoder(base64.StdEncoding, buf)
    60  		io.WriteString(w, s)
    61  		w.Close()
    62  	} else {
    63  		enc := make([]byte, 3)
    64  		for i := 0; i < len(s); i++ {
    65  			b := s[i]
    66  			switch {
    67  			case b == ' ':
    68  				buf.WriteByte('_')
    69  			case b <= '~' && b >= '!' && b != '=' && b != '?' && b != '_':
    70  				buf.WriteByte(b)
    71  			default:
    72  				enc[0] = '='
    73  				enc[1] = upperhex[b>>4]
    74  				enc[2] = upperhex[b&0x0f]
    75  				buf.Write(enc)
    76  			}
    77  		}
    78  	}
    79  	buf.WriteString("?=")
    80  	return buf.String()
    81  }
    82  
    83  const upperhex = "0123456789ABCDEF"
    84  
    85  // A WordDecoder decodes MIME headers containing RFC 2047 encoded-words.
    86  type WordDecoder struct {
    87  	// CharsetReader, if non-nil, defines a function to generate
    88  	// charset-conversion readers, converting from the provided
    89  	// charset into UTF-8.
    90  	// Charsets are always lower-case. utf-8, iso-8859-1 and us-ascii charsets
    91  	// are handled by default.
    92  	// One of the the CharsetReader's result values must be non-nil.
    93  	CharsetReader func(charset string, input io.Reader) (io.Reader, error)
    94  }
    95  
    96  // Decode decodes an encoded-word. If word is not a valid RFC 2047 encoded-word,
    97  // word is returned unchanged.
    98  func (d *WordDecoder) Decode(word string) (string, error) {
    99  	fields := strings.Split(word, "?") // TODO: remove allocation?
   100  	if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" || len(fields[2]) != 1 {
   101  		return "", errInvalidWord
   102  	}
   103  
   104  	content, err := decode(fields[2][0], fields[3])
   105  	if err != nil {
   106  		return "", err
   107  	}
   108  
   109  	buf := getBuffer()
   110  	defer putBuffer(buf)
   111  
   112  	if err := d.convert(buf, fields[1], content); err != nil {
   113  		return "", err
   114  	}
   115  
   116  	return buf.String(), nil
   117  }
   118  
   119  // DecodeHeader decodes all encoded-words of the given string. It returns an
   120  // error if and only if CharsetReader of d returns an error.
   121  func (d *WordDecoder) DecodeHeader(header string) (string, error) {
   122  	// If there is no encoded-word, returns before creating a buffer.
   123  	i := strings.Index(header, "=?")
   124  	if i == -1 {
   125  		return header, nil
   126  	}
   127  
   128  	buf := getBuffer()
   129  	defer putBuffer(buf)
   130  
   131  	buf.WriteString(header[:i])
   132  	header = header[i:]
   133  
   134  	betweenWords := false
   135  	for {
   136  		start := strings.Index(header, "=?")
   137  		if start == -1 {
   138  			break
   139  		}
   140  		cur := start + len("=?")
   141  
   142  		i := strings.Index(header[cur:], "?")
   143  		if i == -1 {
   144  			break
   145  		}
   146  		charset := header[cur : cur+i]
   147  		cur += i + len("?")
   148  
   149  		if len(header) < cur+len("Q??=") {
   150  			break
   151  		}
   152  		encoding := header[cur]
   153  		cur++
   154  
   155  		if header[cur] != '?' {
   156  			break
   157  		}
   158  		cur++
   159  
   160  		j := strings.Index(header[cur:], "?=")
   161  		if j == -1 {
   162  			break
   163  		}
   164  		text := header[cur : cur+j]
   165  		end := cur + j + len("?=")
   166  
   167  		content, err := decode(encoding, text)
   168  		if err != nil {
   169  			betweenWords = false
   170  			buf.WriteString(header[:start+2])
   171  			header = header[start+2:]
   172  			continue
   173  		}
   174  
   175  		// Write characters before the encoded-word. White-space and newline
   176  		// characters separating two encoded-words must be deleted.
   177  		if start > 0 && (!betweenWords || hasNonWhitespace(header[:start])) {
   178  			buf.WriteString(header[:start])
   179  		}
   180  
   181  		if err := d.convert(buf, charset, content); err != nil {
   182  			return "", err
   183  		}
   184  
   185  		header = header[end:]
   186  		betweenWords = true
   187  	}
   188  
   189  	if len(header) > 0 {
   190  		buf.WriteString(header)
   191  	}
   192  
   193  	return buf.String(), nil
   194  }
   195  
   196  func decode(encoding byte, text string) ([]byte, error) {
   197  	switch encoding {
   198  	case 'B', 'b':
   199  		return base64.StdEncoding.DecodeString(text)
   200  	case 'Q', 'q':
   201  		return qDecode(text)
   202  	}
   203  	return nil, errInvalidWord
   204  }
   205  
   206  func (d *WordDecoder) convert(buf *bytes.Buffer, charset string, content []byte) error {
   207  	switch {
   208  	case strings.EqualFold("utf-8", charset):
   209  		buf.Write(content)
   210  	case strings.EqualFold("iso-8859-1", charset):
   211  		for _, c := range content {
   212  			buf.WriteRune(rune(c))
   213  		}
   214  	case strings.EqualFold("us-ascii", charset):
   215  		for _, c := range content {
   216  			if c >= utf8.RuneSelf {
   217  				buf.WriteRune(unicode.ReplacementChar)
   218  			} else {
   219  				buf.WriteByte(c)
   220  			}
   221  		}
   222  	default:
   223  		if d.CharsetReader == nil {
   224  			return fmt.Errorf("mime: unhandled charset %q", charset)
   225  		}
   226  		r, err := d.CharsetReader(strings.ToLower(charset), bytes.NewReader(content))
   227  		if err != nil {
   228  			return err
   229  		}
   230  		if _, err = buf.ReadFrom(r); err != nil {
   231  			return err
   232  		}
   233  	}
   234  	return nil
   235  }
   236  
   237  // hasNonWhitespace reports whether s (assumed to be ASCII) contains at least
   238  // one byte of non-whitespace.
   239  func hasNonWhitespace(s string) bool {
   240  	for _, b := range s {
   241  		switch b {
   242  		// Encoded-words can only be separated by linear white spaces which does
   243  		// not include vertical tabs (\v).
   244  		case ' ', '\t', '\n', '\r':
   245  		default:
   246  			return true
   247  		}
   248  	}
   249  	return false
   250  }
   251  
   252  // qDecode decodes a Q encoded string.
   253  func qDecode(s string) ([]byte, error) {
   254  	dec := make([]byte, len(s))
   255  	n := 0
   256  	for i := 0; i < len(s); i++ {
   257  		switch c := s[i]; {
   258  		case c == '_':
   259  			dec[n] = ' '
   260  		case c == '=':
   261  			if i+2 >= len(s) {
   262  				return nil, errInvalidWord
   263  			}
   264  			b, err := readHexByte(s[i+1], s[i+2])
   265  			if err != nil {
   266  				return nil, err
   267  			}
   268  			dec[n] = b
   269  			i += 2
   270  		case (c <= '~' && c >= ' ') || c == '\n' || c == '\r' || c == '\t':
   271  			dec[n] = c
   272  		default:
   273  			return nil, errInvalidWord
   274  		}
   275  		n++
   276  	}
   277  
   278  	return dec[:n], nil
   279  }