package smtpserver import ( "b612.me/starlog" "b612.me/startext" "bytes" "crypto/tls" "fmt" "github.com/spf13/cobra" "html" "io" "io/ioutil" "mime" "mime/quotedprintable" "net/mail" "os" "strings" "time" "github.com/emersion/go-smtp" ) var addr string var user, pass string var allowAnyuser bool var output string var domain string var cert, key string var Cmd = &cobra.Command{ Use: "smtps", Short: "smtp server", Long: "smtp server", Run: func(cmd *cobra.Command, args []string) { run() }, } func init() { Cmd.Flags().StringVarP(&addr, "addr", "a", "0.0.0.0:25", "smtp server listen address") Cmd.Flags().StringVarP(&user, "user", "u", "admin", "smtp server username") Cmd.Flags().StringVarP(&pass, "pass", "p", "admin", "smtp server password") Cmd.Flags().BoolVarP(&allowAnyuser, "allow-anyuser", "A", false, "allow any user") Cmd.Flags().StringVarP(&output, "output", "o", "", "output mail to html") Cmd.Flags().StringVarP(&domain, "domain", "d", "localhost", "smtp server domain") Cmd.Flags().StringVarP(&cert, "cert", "c", "", "smtp server cert(TLS)") Cmd.Flags().StringVarP(&key, "key", "k", "", "smtp server key(TLS)") } type backend struct{} func (bkd *backend) NewSession(c *smtp.Conn) (smtp.Session, error) { return &session{}, nil } type session struct { username string password string to string } func (s *session) AuthPlain(username, password string) error { s.username = username s.password = password starlog.Printf("username:%s,password:%s\n", username, password) if allowAnyuser { return nil } else { if username != user || password != pass { return smtp.ErrAuthFailed } } return nil } func (s *session) Mail(from string, opts *smtp.MailOptions) error { return nil } func (s *session) Rcpt(to string, opts *smtp.RcptOptions) error { s.to += to + ";" return nil } func (s *session) Data(r io.Reader) error { mailData, err := ioutil.ReadAll(r) if err != nil { return err } msg, err := mail.ReadMessage(bytes.NewReader(mailData)) if err != nil { return err } header := msg.Header subject := header.Get("Subject") to := header.Get("To") cc := header.Get("Cc") from := header.Get("From") // 获取发件人 date := header.Get("Date") body, err := ioutil.ReadAll(msg.Body) if err != nil { return err } var bodyStr string var d = new(mime.WordDecoder) { subject, err = d.DecodeHeader(subject) if err != nil { starlog.Errorf("Decode subject %s error:%s\n", subject, err) return err } bodyStr, err = bodyDecode(string(body)) if err != nil { starlog.Errorf("Decode body %s error:%s\n", string(body), err) return err } } if startext.IsGBK([]byte(bodyStr)) { tmp, err := startext.GBK2UTF8([]byte(bodyStr)) if err == nil { bodyStr = string(tmp) } } starlog.Println("From:", from) starlog.Println("Subject:", subject) starlog.Println("To ALL:", s.to) starlog.Println("To:", to) starlog.Println("Cc:", cc) starlog.Println("Body:", bodyStr) if output != "" { path := fmt.Sprintf("%s/%s_%s.html", output, subject, time.Now().Format("2006_01_02_15_04_05_")) html := fmt.Sprintf(` %s

%s


auth user: %s

auth pass: %s


Date: %v

From: %s

To All: %s

To: %s

Cc: %s




%s `, html.EscapeString(subject), html.EscapeString(subject), html.EscapeString(s.username), html.EscapeString(s.password), date, html.EscapeString(from), html.EscapeString(s.to), html.EscapeString(to), html.EscapeString(cc), bodyStr) os.WriteFile(path, []byte(html), 0644) } return nil } func (s *session) Reset() {} func (s *session) Logout() error { return nil } func run() { var err error s := smtp.NewServer(&backend{}) if cert != "" && key != "" { var config tls.Config config.Certificates = make([]tls.Certificate, 1) config.Certificates[0], err = tls.LoadX509KeyPair(cert, key) if err != nil { starlog.Errorln("failed to load cert:", err) return } s.TLSConfig = &config s.Addr = addr s.Domain = domain s.AllowInsecureAuth = true s.Debug = os.Stdout starlog.Infoln("Starting TLS-SMTP server at", addr) starlog.Errorln(s.ListenAndServeTLS()) return } s.Addr = addr s.Domain = domain s.AllowInsecureAuth = true s.Debug = os.Stdout starlog.Infoln("Starting SMTP server at", addr) starlog.Errorln(s.ListenAndServe()) } func bodyDecode(encoded string) (string, error) { encoded = strings.Replace(encoded, "=\r\n", "", -1) // 对于Windows系统 encoded = strings.Replace(encoded, "=\n", "", -1) // 对于UNIX/Linux系统 // 创建一个新的Quoted-Printable阅读器 reader := quotedprintable.NewReader(strings.NewReader(encoded)) // 读取并解码整个内容 decoded, err := ioutil.ReadAll(reader) if err != nil { fmt.Println("Error decoding string:", err) return string(decoded), err } return string(decoded), nil }