first commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user