@@ -13,6 +13,7 @@ import (
"mime"
"mime/multipart"
"mime/quotedprintable"
+ "net/mail"
"net/smtp"
"net/textproto"
"os"
@@ -106,6 +107,21 @@ func smtpHelloHostname() string {
return hostname
}
+// extractBareEmail extracts just the email address from a formatted address
+// like "Name <email@example.com>" or returns the input if it's already bare.
+// This is needed for SMTP MAIL FROM command which requires only the email address.
+func extractBareEmail(addr string) string {
+ if addr == "" {
+ return ""
+ }
+ parsed, err := mail.ParseAddress(addr)
+ if err != nil {
+ // If parsing fails, return as-is (it might already be bare)
+ return addr
+ }
+ return parsed.Address
+}
+
// generateMessageID creates a unique Message-ID header.
func generateMessageID(from string) string {
buf := make([]byte, 16)
@@ -742,7 +758,7 @@ func SendEmail(account *config.Account, to, cc, bcc []string, subject, plainBody
}
// Send Envelope
- if err = c.Mail(account.GetSendAsEmail()); err != nil {
+ if err = c.Mail(extractBareEmail(account.GetSendAsEmail())); err != nil {
return nil, err
}
for _, r := range allRecipients {
@@ -954,7 +970,7 @@ func SendCalendarReply(account *config.Account, to []string, subject, plainBody
}
}
- if err = c.Mail(account.GetSendAsEmail()); err != nil {
+ if err = c.Mail(extractBareEmail(account.GetSendAsEmail())); err != nil {
return nil, err
}
for _, r := range to {
@@ -130,3 +130,46 @@ func TestGenerateMessageID(t *testing.T) {
t.Errorf("Message-ID has an empty random part, got %s", msgID)
}
}
+
+func TestExtractBareEmail(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expected string
+ }{
+ {
+ name: "bare email",
+ input: "user@example.com",
+ expected: "user@example.com",
+ },
+ {
+ name: "formatted with name",
+ input: "John Doe <user@example.com>",
+ expected: "user@example.com",
+ },
+ {
+ name: "formatted with quoted name",
+ input: "\"John Doe\" <user@example.com>",
+ expected: "user@example.com",
+ },
+ {
+ name: "empty string",
+ input: "",
+ expected: "",
+ },
+ {
+ name: "invalid format returns as-is",
+ input: "not-an-email",
+ expected: "not-an-email",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := extractBareEmail(tt.input)
+ if got != tt.expected {
+ t.Errorf("extractBareEmail(%q) = %q, want %q", tt.input, got, tt.expected)
+ }
+ })
+ }
+}