-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 golang#4943 Change-Id: I5f58aa58e74bbe4ec91b2e9b8c81921338053b00 Reviewed-on: https://go-review.googlesource.com/2101 Reviewed-by: Brad Fitzpatrick <[email protected]>
- Loading branch information
1 parent
2f9c9e5
commit 828129f
Showing
3 changed files
with
126 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <= '~' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters