upgrade deps; rewrite smtp session

This commit is contained in:
Aine
2024-02-19 22:55:14 +02:00
parent 10213cc7d7
commit a01720da00
277 changed files with 106832 additions and 7641 deletions

View File

@@ -24,6 +24,14 @@ const (
te7Bit transferEncoding = iota
teQuoted
teBase64
teRaw
)
const (
base64EncodedLineLen = 76
base64DecodedLineLen = base64EncodedLineLen * 3 / 4 // this is ok since lineLen is divisible by 4
linesPerChunk = 128
readChunkSize = base64DecodedLineLen * linesPerChunk
)
var crnl = []byte{'\r', '\n'}
@@ -33,6 +41,15 @@ func (p *Part) Encode(writer io.Writer) error {
if p.Header == nil {
p.Header = make(textproto.MIMEHeader)
}
if p.ContentReader != nil {
// read some data in order to check whether the content is empty
p.Content = make([]byte, readChunkSize)
n, err := p.ContentReader.Read(p.Content)
if err != nil && err != io.EOF {
return err
}
p.Content = p.Content[:n]
}
cte := p.setupMIMEHeaders()
// Encode this part.
b := bufio.NewWriter(writer)
@@ -82,12 +99,14 @@ func (p *Part) setupMIMEHeaders() transferEncoding {
// If we are encoding a part that previously had content-transfer-encoding set, unset it so
// the correct encoding detection can be done below.
p.Header.Del(hnContentEncoding)
if p.parser != nil && !p.parser.rawContent {
p.Header.Del(hnContentEncoding)
}
cte := te7Bit
if len(p.Content) > 0 {
cte = teBase64
if p.TextContent() {
if p.TextContent() && p.ContentReader == nil {
cte = selectTransferEncoding(p.Content, false)
if p.Charset == "" {
p.Charset = utf8
@@ -104,7 +123,7 @@ func (p *Part) setupMIMEHeaders() transferEncoding {
// Setup headers.
if p.FirstChild != nil && p.Boundary == "" {
// Multipart, generate random boundary marker.
p.Boundary = "enmime-" + stringutil.UUID()
p.Boundary = "enmime-" + stringutil.UUID(p.randSource)
}
if p.ContentID != "" {
p.Header.Set(hnContentID, coding.ToIDHeader(p.ContentID))
@@ -135,7 +154,7 @@ func (p *Part) setupMIMEHeaders() transferEncoding {
param := make(map[string]string)
setParamValue(param, hpFilename, fileName)
if !p.FileModDate.IsZero() {
setParamValue(param, hpModDate, p.FileModDate.Format(time.RFC822))
setParamValue(param, hpModDate, p.FileModDate.UTC().Format(time.RFC822))
}
if mt := mime.FormatMediaType(p.Disposition, param); mt != "" {
p.Disposition = mt
@@ -151,15 +170,19 @@ func (p *Part) encodeHeader(b *bufio.Writer) error {
for k := range p.Header {
keys = append(keys, k)
}
rawContent := p.parser != nil && p.parser.rawContent
sort.Strings(keys)
for _, k := range keys {
for _, v := range p.Header[k] {
encv := v
switch selectTransferEncoding([]byte(v), true) {
case teBase64:
encv = mime.BEncoding.Encode(utf8, v)
case teQuoted:
encv = mime.QEncoding.Encode(utf8, v)
if !rawContent {
switch selectTransferEncoding([]byte(v), true) {
case teBase64:
encv = mime.BEncoding.Encode(utf8, v)
case teQuoted:
encv = mime.QEncoding.Encode(utf8, v)
}
}
// _ used to prevent early wrapping
wb := stringutil.Wrap(76, k, ":_", encv, "\r\n")
@@ -174,11 +197,19 @@ func (p *Part) encodeHeader(b *bufio.Writer) error {
// encodeContent writes out the content in the selected encoding.
func (p *Part) encodeContent(b *bufio.Writer, cte transferEncoding) (err error) {
if p.ContentReader != nil {
return p.encodeContentFromReader(b)
}
if p.parser != nil && p.parser.rawContent {
cte = teRaw
}
switch cte {
case teBase64:
enc := base64.StdEncoding
text := make([]byte, enc.EncodedLen(len(p.Content)))
base64.StdEncoding.Encode(text, p.Content)
enc.Encode(text, p.Content)
// Wrap lines.
lineLen := 76
for len(text) > 0 {
@@ -205,6 +236,54 @@ func (p *Part) encodeContent(b *bufio.Writer, cte transferEncoding) (err error)
return err
}
// encodeContentFromReader writes out the content read from the reader using base64 encoding.
func (p *Part) encodeContentFromReader(b *bufio.Writer) error {
text := make([]byte, base64EncodedLineLen) // a single base64 encoded line
enc := base64.StdEncoding
chunk := make([]byte, readChunkSize) // contains a whole number of lines
copy(chunk, p.Content) // copy the data of the initial read that was issued by `Encode`
n := len(p.Content)
for {
// call read until we get a full chunk / error
for n < len(chunk) {
c, err := p.ContentReader.Read(chunk[n:])
if err != nil {
if err == io.EOF {
break
}
return err
}
n += c
}
for i := 0; i < n; i += base64DecodedLineLen {
size := n - i
if size > base64DecodedLineLen {
size = base64DecodedLineLen
}
enc.Encode(text, chunk[i:i+size])
if _, err := b.Write(text[:enc.EncodedLen(size)]); err != nil {
return err
}
if _, err := b.Write(crnl); err != nil {
return err
}
}
if n < len(chunk) {
break
}
n = 0
}
return nil
}
// selectTransferEncoding scans content for non-ASCII characters and selects 'b' or 'q' encoding.
func selectTransferEncoding(content []byte, quoteLineBreaks bool) transferEncoding {
if len(content) == 0 {