Files

82 lines
2.2 KiB
Go
Raw Permalink Normal View History

2026-08-09 18:03:09 +01:00
package accounts
import (
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/smtp"
"strconv"
"gomail/internal/crypto"
"gomail/internal/db"
)
// sendViaSMTP delivers an outgoing message through a linked account's own
// SMTP settings — stdlib net/smtp, same choice as internal/queue's
// MXDeliverer, so outbound for both "GoMail relays for me" and "I'm using my
// own IMAP+SMTP provider" stay on the same dependency-free foundation.
func sendViaSMTP(account *db.LinkedAccount, mk *crypto.MasterKey, msg *OutgoingMessage) error {
plain, err := crypto.Decrypt(mk, account.ID, "linked-account-cred", account.CredentialEnc)
if err != nil {
return fmt.Errorf("decrypting stored credential: %w", err)
}
var cred IMAPCredential // same password shape reused for the paired SMTP auth
if err := json.Unmarshal(plain, &cred); err != nil {
return fmt.Errorf("parsing stored credential: %w", err)
}
addr := net.JoinHostPort(account.SMTPHost, strconv.Itoa(account.SMTPPort))
conn, err := net.DialTimeout("tcp", addr, dialTimeout)
if err != nil {
return fmt.Errorf("dial %s: %w", addr, err)
}
defer conn.Close()
if account.SMTPTLS == "implicit" {
// See provider_imap.go's connect() comment — same gap, same reason.
conn = tls.Client(conn, &tls.Config{ServerName: account.SMTPHost, InsecureSkipVerify: true})
}
client, err := smtp.NewClient(conn, account.SMTPHost)
if err != nil {
return fmt.Errorf("SMTP handshake: %w", err)
}
defer client.Close()
if account.SMTPTLS == "starttls" {
if ok, _ := client.Extension("STARTTLS"); ok {
if err := client.StartTLS(&tls.Config{ServerName: account.SMTPHost}); err != nil {
return fmt.Errorf("STARTTLS: %w", err)
}
}
}
auth := smtp.PlainAuth("", account.EmailAddress, cred.Password, account.SMTPHost)
if err := client.Auth(auth); err != nil {
return fmt.Errorf("SMTP auth: %w", err)
}
if err := client.Mail(account.EmailAddress); err != nil {
return err
}
for _, to := range msg.To {
if err := client.Rcpt(to); err != nil {
return err
}
}
w, err := client.Data()
if err != nil {
return err
}
raw := buildRFC5322(account.EmailAddress, msg)
if _, err := w.Write(raw); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}
return client.Quit()
}