104 lines
2.7 KiB
Go
104 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"net/smtp"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
smtpHost := getenv("SMTP_HOST", "smtp.163.com")
|
|
smtpPort := getenv("SMTP_PORT", "465")
|
|
smtpUser := strings.TrimSpace(os.Getenv("SMTP_USER"))
|
|
smtpPass := strings.TrimSpace(os.Getenv("SMTP_PASS"))
|
|
mailTo := strings.TrimSpace(os.Getenv("MAIL_TO"))
|
|
subject := getenv("MAIL_SUBJECT", "SuperTodo SMTP test")
|
|
body := getenv("MAIL_BODY", "This is a SMTP connectivity test email from SuperTodo.")
|
|
|
|
if smtpUser == "" || smtpPass == "" || mailTo == "" {
|
|
log.Fatal("missing required env: SMTP_USER, SMTP_PASS, MAIL_TO")
|
|
}
|
|
|
|
if err := sendMailTLS(smtpHost, smtpPort, smtpUser, smtpPass, mailTo, subject, body); err != nil {
|
|
log.Fatalf("send mail failed: %v", err)
|
|
}
|
|
|
|
log.Printf("mail sent successfully: from=%s to=%s via %s:%s", smtpUser, mailTo, smtpHost, smtpPort)
|
|
}
|
|
|
|
func sendMailTLS(host, port, user, pass, to, subject, body string) error {
|
|
addr := net.JoinHostPort(host, port)
|
|
dialer := &net.Dialer{Timeout: 10 * time.Second}
|
|
conn, err := tls.DialWithDialer(dialer, "tcp", addr, &tls.Config{
|
|
ServerName: host,
|
|
MinVersion: tls.VersionTLS12,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("tls dial failed: %w", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
client, err := smtp.NewClient(conn, host)
|
|
if err != nil {
|
|
return fmt.Errorf("smtp client failed: %w", err)
|
|
}
|
|
defer client.Close()
|
|
|
|
auth := smtp.PlainAuth("", user, pass, host)
|
|
if err := client.Auth(auth); err != nil {
|
|
return fmt.Errorf("smtp auth failed: %w", err)
|
|
}
|
|
if err := client.Mail(user); err != nil {
|
|
return fmt.Errorf("mail from failed: %w", err)
|
|
}
|
|
if err := client.Rcpt(to); err != nil {
|
|
return fmt.Errorf("rcpt to failed: %w", err)
|
|
}
|
|
|
|
w, err := client.Data()
|
|
if err != nil {
|
|
return fmt.Errorf("data failed: %w", err)
|
|
}
|
|
|
|
msg := buildMessage(user, to, subject, body)
|
|
if _, err := w.Write([]byte(msg)); err != nil {
|
|
return fmt.Errorf("write message failed: %w", err)
|
|
}
|
|
if err := w.Close(); err != nil {
|
|
return fmt.Errorf("close writer failed: %w", err)
|
|
}
|
|
if err := client.Quit(); err != nil {
|
|
return fmt.Errorf("smtp quit failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func buildMessage(from, to, subject, body string) string {
|
|
var b strings.Builder
|
|
b.WriteString("From: " + from + "\r\n")
|
|
b.WriteString("To: " + to + "\r\n")
|
|
b.WriteString("Subject: " + subject + "\r\n")
|
|
b.WriteString("MIME-Version: 1.0\r\n")
|
|
b.WriteString("Content-Type: text/plain; charset=UTF-8\r\n")
|
|
b.WriteString("\r\n")
|
|
b.WriteString(body)
|
|
if !strings.HasSuffix(body, "\n") {
|
|
b.WriteString("\r\n")
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func getenv(key, fallback string) string {
|
|
v := strings.TrimSpace(os.Getenv(key))
|
|
if v == "" {
|
|
return fallback
|
|
}
|
|
return v
|
|
}
|
|
|