forked from fevenor/smtp2webhook
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.go
150 lines (127 loc) · 3.09 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package main
import (
"errors"
"io"
"log"
"net/http"
"net/url"
"os"
"strings"
"text/template"
"time"
"github.com/emersion/go-smtp"
"github.com/jordan-wright/email"
"go.uber.org/zap"
"jaytaylor.com/html2text"
)
var sugar *zap.SugaredLogger
var user string
var passwd string
var webhook *template.Template
// The Backend implements SMTP server methods.
type Backend struct{}
// NewSession is called after client greeting (EHLO, HELO).
func (bkd *Backend) NewSession(c *smtp.Conn) (smtp.Session, error) {
sugar.Debug("New Connect: ", c.Conn().RemoteAddr().String())
return &Session{}, nil
}
// A Session is returned after successful login.
type Session struct {
}
// AuthPlain implements authentication using SASL PLAIN.
func (s *Session) AuthPlain(username, password string) error {
if username != user || password != passwd {
sugar.Error("invalid username or password")
return errors.New("invalid username or password")
}
return nil
}
func (s *Session) Mail(from string, _ *smtp.MailOptions) error {
sugar.Debug("Mail from:", from)
return nil
}
func (s *Session) Rcpt(to string, _ *smtp.RcptOptions) error {
sugar.Debug("Rcpt to:", to)
return nil
}
func (s *Session) Data(r io.Reader) (err error) {
title, content, err := ReadData(r)
if err != nil {
sugar.Error("ReadData error ", err)
return err
}
err = CallWebhook(title, content)
return
}
func (s *Session) Reset() {}
func (s *Session) Logout() error { return nil }
func ReadData(r io.Reader) (title, content string, err error) {
e, err := email.NewEmailFromReader(r)
if err != nil {
return "", "", err
}
title = e.Subject
if e.Text != nil {
content = string(e.Text)
} else {
content, err = html2text.FromString(string(e.HTML), html2text.Options{PrettyTables: true})
if err != nil {
return "", "", err
}
}
sugar.Debug("Data:", content)
return
}
func CallWebhook(title, content string) (err error) {
var urlSb strings.Builder
err = webhook.Execute(&urlSb, map[string]string{
"title": url.QueryEscape(title),
"content": url.QueryEscape(content),
})
if err != nil {
return err
}
urlAddr := urlSb.String()
var resp *http.Response
resp, err = http.Get(urlAddr)
if err != nil {
sugar.Error("Call Webhook error! ", err)
} else {
sugar.Info("Call Webhook success:", resp)
}
return
}
func main() {
logger, err := zap.NewProduction()
if err != nil {
log.Fatalln(err)
}
defer func(logger *zap.Logger) {
err := logger.Sync()
if err != nil {
log.Fatalln(err)
}
}(logger)
sugar = logger.Sugar()
user = os.Getenv("USERNAME")
passwd = os.Getenv("PASSWORD")
webhook, err = template.New("url").Parse(os.Getenv("WEBHOOK"))
if err != nil {
sugar.Error("webhook template create error ", err)
os.Exit(1)
}
be := &Backend{}
s := smtp.NewServer(be)
s.Addr = "0.0.0.0:8587"
s.Domain = "fevenor.com"
s.WriteTimeout = 10 * time.Second
s.ReadTimeout = 10 * time.Second
s.MaxMessageBytes = 1024 * 1024
s.MaxRecipients = 50
s.AllowInsecureAuth = true
sugar.Info("Starting server at ", s.Addr)
if err := s.ListenAndServe(); err != nil {
sugar.Error("SMTP server start error ", err)
os.Exit(1)
}
}