Compare commits

...

2 Commits

Author SHA1 Message Date
Peter Steinberger
295dc4f951 fix(gmail): land charset fallback note (#428) (thanks @WinnCook)
Some checks failed
ci / test (push) Has been cancelled
ci / worker (push) Has been cancelled
ci / windows (push) Has been cancelled
ci / darwin-cgo-build (push) Has been cancelled
2026-03-07 17:51:46 +00:00
doodaaatimmy-creator
6489dd9fe3 fix(gmail): decode mime-type charset fallback 2026-03-07 17:51:02 +00:00
3 changed files with 42 additions and 1 deletions

View File

@ -19,6 +19,7 @@
- Auth: keep Keep-only service-account fallback isolated to Keep commands so other Google services do not accidentally pick it up. (#414) — thanks @jgwesterlund.
- Contacts: send the required `copyMask` when deleting "other contacts", avoiding People API 400 errors. (#384) — thanks @rbansal42.
- Calendar: hide cancelled/deleted events from `calendar events` list output by explicitly setting `showDeleted=false`. (#362) — thanks @sharukh010.
- Gmail: fall back to `MimeType` charset hints when `Content-Type` headers are missing so GBK/GB2312 message bodies decode correctly. (#428) — thanks @WinnCook.
- Gmail: add a fetch delay in `watch serve` so History API reads don't race message indexing. (#397) — thanks @salmonumbrella.
- Gmail: allow Workspace-managed send-as aliases with empty verification status in `send` and `drafts create`. (#407) — thanks @salmonumbrella.
- Gmail: preserve the selected `--client` during `watch serve` push handling instead of falling back to the default client. (#411) — thanks @chrysb.

View File

@ -462,7 +462,11 @@ func decodePartBody(p *gmail.MessagePart) (string, error) {
decoded = decodeTransferEncoding(decoded, cte)
}
if contentType := strings.TrimSpace(headerValue(p, "Content-Type")); contentType != "" {
contentType := strings.TrimSpace(headerValue(p, "Content-Type"))
if contentType == "" {
contentType = strings.TrimSpace(p.MimeType)
}
if contentType != "" {
decoded = decodeBodyCharset(decoded, contentType)
}

View File

@ -7,7 +7,9 @@ import (
"path/filepath"
"testing"
"golang.org/x/text/encoding/ianaindex"
"golang.org/x/text/encoding/japanese"
"golang.org/x/text/encoding/simplifiedchinese"
"google.golang.org/api/gmail/v1"
)
@ -297,6 +299,40 @@ func TestDecodeBodyCharset_ISO2022JP_TruncatedEscapeSequence(t *testing.T) {
}
}
func TestDecodeBodyCharset_GBK(t *testing.T) {
source := "您的阿里云账户已欠费即将停服提醒"
enc, err := ianaindex.MIME.Encoding("gbk")
if err != nil || enc == nil {
t.Fatalf("lookup gbk encoding: %v", err)
}
encoded, err := enc.NewEncoder().Bytes([]byte(source))
if err != nil {
t.Fatalf("encode gbk: %v", err)
}
got := decodeBodyCharset(encoded, "text/plain; charset=gbk")
if string(got) != source {
t.Fatalf("unexpected decoded charset: expected %q, got %q", source, string(got))
}
}
func TestFindPartBody_UsesMimeTypeCharsetWhenHeaderMissing(t *testing.T) {
source := "您的阿里云账户已欠费即将停服提醒"
encodedBody, err := simplifiedchinese.GBK.NewEncoder().Bytes([]byte(source))
if err != nil {
t.Fatalf("encode gb2312: %v", err)
}
part := &gmail.MessagePart{
MimeType: "text/plain; charset=gb2312",
Body: &gmail.MessagePartBody{
Data: base64.RawURLEncoding.EncodeToString(encodedBody),
},
}
got := findPartBody(part, "text/plain")
if got != source {
t.Fatalf("unexpected decoded body: expected %q, got %q", source, got)
}
}
func TestMimeTypeMatches(t *testing.T) {
if !mimeTypeMatches("Text/Plain; charset=UTF-8", "text/plain") {
t.Fatalf("expected mime match")