A minimal email TUI where you read with Markdown and write in Neovim. neomd.ssp.sh/docs
email markdown neovim tui
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix umlaut and charset if different used in Outlook or EU

sspaeti 023e42ea 37944a47

+87
+1
internal/imap/client.go
··· 19 19 imap "github.com/emersion/go-imap/v2" 20 20 "github.com/emersion/go-imap/v2/imapclient" 21 21 "github.com/emersion/go-message" 22 + _ "github.com/emersion/go-message/charset" // register charset decoders for ISO-8859-1, Windows-1252, etc. 22 23 "github.com/emersion/go-message/mail" 23 24 "github.com/sspaeti/neomd/internal/mailtls" 24 25 "github.com/sspaeti/neomd/internal/oauth2"
+86
internal/imap/client_test.go
··· 2 2 3 3 import ( 4 4 "context" 5 + "encoding/base64" 5 6 "strings" 6 7 "testing" 7 8 "time" ··· 541 542 542 543 if body == "" { 543 544 t.Error("parseBody returned empty body for multipart with unknown charset") 545 + } 546 + } 547 + 548 + func TestParseBody_ISO88591_Base64(t *testing.T) { 549 + // Real-world case: Swiss bank email with charset=iso-8859-1 and base64 encoding. 550 + // German umlauts (ü, ä, ö) must decode correctly to UTF-8. 551 + // In ISO-8859-1: ü=0xFC, ä=0xE4, ö=0xF6, Ü=0xDC 552 + iso88591Bytes := []byte("H\xe4b \xe4 sch\xf6ne Abe\r\n\xdcber CHF 7'260") 553 + encoded := base64.StdEncoding.EncodeToString(iso88591Bytes) 554 + 555 + raw := "MIME-Version: 1.0\r\n" + 556 + "Content-Type: text/plain; charset=iso-8859-1\r\n" + 557 + "Content-Transfer-Encoding: base64\r\n" + 558 + "\r\n" + 559 + encoded + "\r\n" 560 + 561 + body, _, _, _, _, _ := parseBody([]byte(raw)) 562 + 563 + for _, want := range []string{"Häb", "schöne", "Über"} { 564 + if !strings.Contains(body, want) { 565 + t.Errorf("ISO-8859-1 body missing %q, got: %q", want, body) 566 + } 567 + } 568 + } 569 + 570 + func TestParseBody_Windows1252(t *testing.T) { 571 + // Windows-1252 is common in Outlook-generated emails. 572 + // It extends ISO-8859-1 with characters like curly quotes and em-dash. 573 + // €=0x80, –=0x96, "=0x93, "=0x94 574 + win1252Bytes := []byte("Price: \x804'500 \x93special offer\x94 \x96 limited") 575 + encoded := base64.StdEncoding.EncodeToString(win1252Bytes) 576 + 577 + raw := "MIME-Version: 1.0\r\n" + 578 + "Content-Type: text/plain; charset=windows-1252\r\n" + 579 + "Content-Transfer-Encoding: base64\r\n" + 580 + "\r\n" + 581 + encoded + "\r\n" 582 + 583 + body, _, _, _, _, _ := parseBody([]byte(raw)) 584 + 585 + if !strings.Contains(body, "€") { 586 + t.Errorf("Windows-1252 body missing euro sign €, got: %q", body) 587 + } 588 + if !strings.Contains(body, "\u201c") || !strings.Contains(body, "\u201d") { 589 + t.Logf("Windows-1252 curly quotes may not be preserved, body: %q", body) 590 + } 591 + } 592 + 593 + func TestParseBody_ISO885915(t *testing.T) { 594 + // ISO-8859-15 is used in French/Finnish email. It adds € (0xA4) and 595 + // other characters missing from ISO-8859-1. 596 + iso885915Bytes := []byte("Cr\xe8me br\xfbl\xe9e co\xfbte \xa4100") 597 + encoded := base64.StdEncoding.EncodeToString(iso885915Bytes) 598 + 599 + raw := "MIME-Version: 1.0\r\n" + 600 + "Content-Type: text/plain; charset=iso-8859-15\r\n" + 601 + "Content-Transfer-Encoding: base64\r\n" + 602 + "\r\n" + 603 + encoded + "\r\n" 604 + 605 + body, _, _, _, _, _ := parseBody([]byte(raw)) 606 + 607 + for _, want := range []string{"Crème", "brûlée", "coûte"} { 608 + if !strings.Contains(body, want) { 609 + t.Errorf("ISO-8859-15 body missing %q, got: %q", want, body) 610 + } 611 + } 612 + } 613 + 614 + func TestParseBody_QuotedPrintable_ISO88591(t *testing.T) { 615 + // Same charset but with quoted-printable encoding instead of base64. 616 + // Common in inline replies and older mail clients. 617 + raw := "MIME-Version: 1.0\r\n" + 618 + "Content-Type: text/plain; charset=iso-8859-1\r\n" + 619 + "Content-Transfer-Encoding: quoted-printable\r\n" + 620 + "\r\n" + 621 + "Gr=FC=DFe aus Z=FCrich\r\n" 622 + 623 + body, _, _, _, _, _ := parseBody([]byte(raw)) 624 + 625 + if !strings.Contains(body, "Grüße") { 626 + t.Errorf("QP ISO-8859-1 body missing 'Grüße', got: %q", body) 627 + } 628 + if !strings.Contains(body, "Zürich") { 629 + t.Errorf("QP ISO-8859-1 body missing 'Zürich', got: %q", body) 544 630 } 545 631 } 546 632