mirror of https://github.com/golang/go.git
net/mail: move RFC 2047 code to internal/mime
The code concerning quoted-printable encoding (RFC 2045) and its variant for MIME headers (RFC 2047) is currently spread in mime/multipart and net/mail. It is also not exported. This commit is the second step to fix that issue. It moves the RFC 2047 encoding and decoding functions from net/mail to internal/mime. The exported API is unchanged. Updates #4943 Change-Id: I5f58aa58e74bbe4ec91b2e9b8c81921338053b00 Reviewed-on: https://go-review.googlesource.com/2101 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
2f9c9e552d
commit
828129fdbc
|
|
@ -252,7 +252,7 @@ var pkgDeps = map[string][]string{
|
|||
|
||||
// Uses of networking.
|
||||
"log/syslog": {"L4", "OS", "net"},
|
||||
"net/mail": {"L4", "NET", "OS"},
|
||||
"net/mail": {"L4", "NET", "OS", "internal/mime"},
|
||||
"net/textproto": {"L4", "OS", "net"},
|
||||
|
||||
// Core crypto.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,122 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package mime
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// EncodeWord encodes a string into an RFC 2047 encoded-word.
|
||||
func EncodeWord(s string) string {
|
||||
// UTF-8 "Q" encoding
|
||||
b := bytes.NewBufferString("=?utf-8?q?")
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch c := s[i]; {
|
||||
case c == ' ':
|
||||
b.WriteByte('_')
|
||||
case isVchar(c) && c != '=' && c != '?' && c != '_':
|
||||
b.WriteByte(c)
|
||||
default:
|
||||
fmt.Fprintf(b, "=%02X", c)
|
||||
}
|
||||
}
|
||||
b.WriteString("?=")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// DecodeWord decodes an RFC 2047 encoded-word.
|
||||
func DecodeWord(s string) (string, error) {
|
||||
fields := strings.Split(s, "?")
|
||||
if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" {
|
||||
return "", errors.New("address not RFC 2047 encoded")
|
||||
}
|
||||
charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2])
|
||||
if charset != "us-ascii" && charset != "iso-8859-1" && charset != "utf-8" {
|
||||
return "", fmt.Errorf("charset not supported: %q", charset)
|
||||
}
|
||||
|
||||
in := bytes.NewBufferString(fields[3])
|
||||
var r io.Reader
|
||||
switch enc {
|
||||
case "b":
|
||||
r = base64.NewDecoder(base64.StdEncoding, in)
|
||||
case "q":
|
||||
r = qDecoder{r: in}
|
||||
default:
|
||||
return "", fmt.Errorf("RFC 2047 encoding not supported: %q", enc)
|
||||
}
|
||||
|
||||
dec, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch charset {
|
||||
case "us-ascii":
|
||||
b := new(bytes.Buffer)
|
||||
for _, c := range dec {
|
||||
if c >= 0x80 {
|
||||
b.WriteRune(unicode.ReplacementChar)
|
||||
} else {
|
||||
b.WriteRune(rune(c))
|
||||
}
|
||||
}
|
||||
return b.String(), nil
|
||||
case "iso-8859-1":
|
||||
b := new(bytes.Buffer)
|
||||
for _, c := range dec {
|
||||
b.WriteRune(rune(c))
|
||||
}
|
||||
return b.String(), nil
|
||||
case "utf-8":
|
||||
return string(dec), nil
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
type qDecoder struct {
|
||||
r io.Reader
|
||||
scratch [2]byte
|
||||
}
|
||||
|
||||
func (qd qDecoder) Read(p []byte) (n int, err error) {
|
||||
// This method writes at most one byte into p.
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if _, err := qd.r.Read(qd.scratch[:1]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch c := qd.scratch[0]; {
|
||||
case c == '=':
|
||||
if _, err := io.ReadFull(qd.r, qd.scratch[:2]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
x, err := strconv.ParseInt(string(qd.scratch[:2]), 16, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("mime: invalid RFC 2047 encoding: %q", qd.scratch[:2])
|
||||
}
|
||||
p[0] = byte(x)
|
||||
case c == '_':
|
||||
p[0] = ' '
|
||||
default:
|
||||
p[0] = c
|
||||
}
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
// isVchar returns true if c is an RFC 5322 VCHAR character.
|
||||
func isVchar(c byte) bool {
|
||||
// Visible (printing) characters.
|
||||
return '!' <= c && c <= '~'
|
||||
}
|
||||
|
|
@ -18,17 +18,14 @@ package mail
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/mime"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/textproto"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var debug = debugT(false)
|
||||
|
|
@ -180,21 +177,7 @@ func (a *Address) String() string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
// UTF-8 "Q" encoding
|
||||
b := bytes.NewBufferString("=?utf-8?q?")
|
||||
for i := 0; i < len(a.Name); i++ {
|
||||
switch c := a.Name[i]; {
|
||||
case c == ' ':
|
||||
b.WriteByte('_')
|
||||
case isVchar(c) && c != '=' && c != '?' && c != '_':
|
||||
b.WriteByte(c)
|
||||
default:
|
||||
fmt.Fprintf(b, "=%02X", c)
|
||||
}
|
||||
}
|
||||
b.WriteString("?= ")
|
||||
b.WriteString(s)
|
||||
return b.String()
|
||||
return mime.EncodeWord(a.Name) + " " + s
|
||||
}
|
||||
|
||||
type addrParser []byte
|
||||
|
|
@ -352,7 +335,7 @@ func (p *addrParser) consumePhrase() (phrase string, err error) {
|
|||
|
||||
// RFC 2047 encoded-word starts with =?, ends with ?=, and has two other ?s.
|
||||
if err == nil && strings.HasPrefix(word, "=?") && strings.HasSuffix(word, "?=") && strings.Count(word, "?") == 4 {
|
||||
word, err = decodeRFC2047Word(word)
|
||||
word, err = mime.DecodeWord(word)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -440,86 +423,6 @@ func (p *addrParser) len() int {
|
|||
return len(*p)
|
||||
}
|
||||
|
||||
func decodeRFC2047Word(s string) (string, error) {
|
||||
fields := strings.Split(s, "?")
|
||||
if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" {
|
||||
return "", errors.New("address not RFC 2047 encoded")
|
||||
}
|
||||
charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2])
|
||||
if charset != "us-ascii" && charset != "iso-8859-1" && charset != "utf-8" {
|
||||
return "", fmt.Errorf("charset not supported: %q", charset)
|
||||
}
|
||||
|
||||
in := bytes.NewBufferString(fields[3])
|
||||
var r io.Reader
|
||||
switch enc {
|
||||
case "b":
|
||||
r = base64.NewDecoder(base64.StdEncoding, in)
|
||||
case "q":
|
||||
r = qDecoder{r: in}
|
||||
default:
|
||||
return "", fmt.Errorf("RFC 2047 encoding not supported: %q", enc)
|
||||
}
|
||||
|
||||
dec, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch charset {
|
||||
case "us-ascii":
|
||||
b := new(bytes.Buffer)
|
||||
for _, c := range dec {
|
||||
if c >= 0x80 {
|
||||
b.WriteRune(unicode.ReplacementChar)
|
||||
} else {
|
||||
b.WriteRune(rune(c))
|
||||
}
|
||||
}
|
||||
return b.String(), nil
|
||||
case "iso-8859-1":
|
||||
b := new(bytes.Buffer)
|
||||
for _, c := range dec {
|
||||
b.WriteRune(rune(c))
|
||||
}
|
||||
return b.String(), nil
|
||||
case "utf-8":
|
||||
return string(dec), nil
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
type qDecoder struct {
|
||||
r io.Reader
|
||||
scratch [2]byte
|
||||
}
|
||||
|
||||
func (qd qDecoder) Read(p []byte) (n int, err error) {
|
||||
// This method writes at most one byte into p.
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if _, err := qd.r.Read(qd.scratch[:1]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch c := qd.scratch[0]; {
|
||||
case c == '=':
|
||||
if _, err := io.ReadFull(qd.r, qd.scratch[:2]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
x, err := strconv.ParseInt(string(qd.scratch[:2]), 16, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("mail: invalid RFC 2047 encoding: %q", qd.scratch[:2])
|
||||
}
|
||||
p[0] = byte(x)
|
||||
case c == '_':
|
||||
p[0] = ' '
|
||||
default:
|
||||
p[0] = c
|
||||
}
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||
"abcdefghijklmnopqrstuvwxyz" +
|
||||
"0123456789" +
|
||||
|
|
|
|||
Loading…
Reference in New Issue