-
Notifications
You must be signed in to change notification settings - Fork 100
/
inspect.go
84 lines (75 loc) · 2.19 KB
/
inspect.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package enmime
import (
"bufio"
"bytes"
"io"
"github.com/jhillyerd/enmime/v2/internal/coding"
"github.com/jhillyerd/enmime/v2/internal/textproto"
"github.com/pkg/errors"
)
var defaultHeadersList = []string{
"From",
"To",
"Sender",
"CC",
"BCC",
"Subject",
"Date",
}
// DecodeRFC2047 decodes the given string according to RFC 2047 and returns the
// decoded UTF-8 equivalent. If the input is not using RFC 2047 encoding, or the
// charset is not recognized, it will return the input unmodified.
func DecodeRFC2047(s string) string {
return coding.RFC2047Decode(s)
}
// DecodeHeaders returns a limited selection of mime headers for use by user agents
// Default header list:
//
// "Date", "Subject", "Sender", "From", "To", "CC" and "BCC"
//
// Additional headers provided will be formatted canonically:
//
// h, err := enmime.DecodeHeaders(b, "content-type", "user-agent")
func DecodeHeaders(b []byte, addtlHeaders ...string) (textproto.MIMEHeader, error) {
b = ensureHeaderBoundary(b)
tr := textproto.NewReader(bufio.NewReader(bytes.NewReader(b)))
headers, err := tr.ReadMIMEHeader()
switch errors.Cause(err) {
case nil, io.EOF:
// carry on, io.EOF is expected
default:
return nil, err
}
headerList := defaultHeadersList
headerList = append(headerList, addtlHeaders...)
res := map[string][]string{}
for _, header := range headerList {
h := textproto.CanonicalEmailMIMEHeaderKey(header)
res[h] = make([]string, 0, len(headers[h]))
for _, value := range headers[h] {
res[h] = append(res[h], DecodeRFC2047(value))
}
}
return res, nil
}
// ensureHeaderBoundary scans through an rfc822 document to ensure the boundary between headers and body exists
func ensureHeaderBoundary(b []byte) []byte {
slice := bytes.SplitAfter(b, []byte{'\r', '\n'})
dest := make([]byte, 0, len(b)+2)
headers := true
for _, v := range slice {
if headers && (bytes.Contains(v, []byte{':'}) || bytes.HasPrefix(v, []byte{' '}) || bytes.HasPrefix(v, []byte{'\t'})) {
dest = append(dest, v...)
continue
}
if headers {
headers = false
if !bytes.Equal(v, []byte{'\r', '\n'}) {
dest = append(dest, append([]byte{'\r', '\n'}, v...)...)
continue
}
}
dest = append(dest, v...)
}
return dest
}