]>
src.bluestatic.org Git - mailpopbox.git/blob - smtp/conn.go
16 stateNew state
= iota // Before EHLO.
23 type connection
struct {
36 mailFrom
*mail
.Address
40 func AcceptConnection(netConn net
.Conn
, server Server
) error
{
43 tp
: textproto
.NewConn(netConn
),
44 remoteAddr
: netConn
.RemoteAddr(),
50 conn
.writeReply(220, fmt
.Sprintf("%s ESMTP [%s] (mailpopbox)", server
.Name(), netConn
.LocalAddr()))
53 conn
.line
, err
= conn
.tp
.ReadLine()
55 conn
.writeReply(500, "line too long")
60 if _
, err
= fmt
.Sscanf(conn
.line
, "%s", &cmd
); err
!= nil {
61 conn
.reply(ReplyBadSyntax
)
65 switch strings
.ToUpper(cmd
) {
67 conn
.writeReply(221, "Goodbye")
85 conn
.writeReply(252, "I'll do my best")
87 conn
.writeReply(550, "access denied")
91 conn
.writeReply(250, "https://tools.ietf.org/html/rfc5321")
93 conn
.writeReply(500, "unrecognized command")
100 func (conn
*connection
) reply(reply ReplyLine
) {
101 conn
.writeReply(reply
.Code
, reply
.Message
)
104 func (conn
*connection
) writeReply(code
int, msg
string) {
106 conn
.tp
.PrintfLine("%d %s", code
, msg
)
108 conn
.tp
.PrintfLine("%d", code
)
112 // parsePath parses out either a forward-, reverse-, or return-path from the
113 // current connection line. Returns a (valid-path, ReplyOK) if it was
114 // successfully parsed.
115 func (conn
*connection
) parsePath(command
string) (string, ReplyLine
) {
116 if len(conn
.line
) < len(command
) {
117 return "", ReplyBadSyntax
119 if strings
.ToUpper(command
) != strings
.ToUpper(conn
.line
[:len(command
)]) {
120 return "", ReplyLine
{500, "unrecognized command"}
122 return conn
.line
[len(command
):], ReplyOK
125 func (conn
*connection
) doEHLO() {
129 _
, err
:= fmt
.Sscanf(conn
.line
, "%s %s", &cmd
, &conn
.ehlo
)
131 conn
.reply(ReplyBadSyntax
)
136 conn
.writeReply(250, fmt
.Sprintf("Hello %s [%s]", conn
.ehlo
, conn
.remoteAddr
))
138 conn
.tp
.PrintfLine("250-Hello %s [%s]", conn
.ehlo
, conn
.remoteAddr
)
139 if conn
.server
.TLSConfig() != nil {
140 conn
.tp
.PrintfLine("250-STARTTLS")
142 conn
.tp
.PrintfLine("250 SIZE %d", 40960000)
145 conn
.state
= stateInitial
148 func (conn
*connection
) doMAIL() {
149 if conn
.state
!= stateInitial
{
150 conn
.reply(ReplyBadSequence
)
154 mailFrom
, reply
:= conn
.parsePath("MAIL FROM:")
155 if reply
!= ReplyOK
{
161 conn
.mailFrom
, err
= mail
.ParseAddress(mailFrom
)
163 conn
.reply(ReplyBadSyntax
)
167 conn
.state
= stateMail
171 func (conn
*connection
) doRCPT() {
172 if conn
.state
!= stateMail
&& conn
.state
!= stateRecipient
{
173 conn
.reply(ReplyBadSequence
)
177 rcptTo
, reply
:= conn
.parsePath("RCPT TO:")
178 if reply
!= ReplyOK
{
183 address
, err
:= mail
.ParseAddress(rcptTo
)
185 conn
.reply(ReplyBadSyntax
)
188 if reply
:= conn
.server
.VerifyAddress(*address
); reply
!= ReplyOK
{
193 conn
.rcptTo
= append(conn
.rcptTo
, *address
)
195 conn
.state
= stateRecipient
199 func (conn
*connection
) doDATA() {
200 if conn
.state
!= stateRecipient
{
201 conn
.reply(ReplyBadSequence
)
205 conn
.writeReply(354, "Start mail input; end with <CRLF>.<CRLF>")
207 data
, err
:= conn
.tp
.ReadDotBytes()
210 conn
.writeReply(552, "transaction failed")
214 received
:= time
.Now()
216 RemoteAddr
: conn
.remoteAddr
,
218 MailFrom
: *conn
.mailFrom
,
221 ID
: conn
.envelopeID(received
),
224 trace
:= conn
.getReceivedInfo(env
)
226 env
.Data
= append(trace
, data
...)
228 if reply
:= conn
.server
.OnMessageDelivered(env
); reply
!= nil {
233 conn
.state
= stateInitial
237 func (conn
*connection
) envelopeID(t time
.Time
) string {
239 rand
.Read(idBytes
[:])
240 return fmt
.Sprintf("m.%d.%x", t
.UnixNano(), idBytes
)
243 func (conn
*connection
) getReceivedInfo(envelope Envelope
) []byte {
244 rhost
, _
, err
:= net
.SplitHostPort(conn
.remoteAddr
.String())
246 rhost
= conn
.remoteAddr
.String()
249 rhosts
, err
:= net
.LookupAddr(rhost
)
251 rhost
= fmt
.Sprintf("%s [%s]", rhosts
[0], rhost
)
254 base
:= fmt
.Sprintf("Received: from %s (%s)\r\n ", conn
.ehlo
, rhost
)
263 base
+= fmt
.Sprintf("by %s (mailpopbox) with %s id %s\r\n ", conn
.server
.Name(), with
, envelope
.ID
)
265 base
+= fmt
.Sprintf("for <%s>\r\n ", envelope
.RcptTo
[0].Address
)
267 transport
:= "PLAINTEXT"
269 // TODO: TLS version, cipher, bits
271 date
:= envelope
.Received
.Format(time
.RFC1123Z
) // Same as RFC 5322 ยง 3.3
272 base
+= fmt
.Sprintf("(using %s);\r\n %s\r\n", transport
, date
)
277 func (conn
*connection
) doRSET() {
278 conn
.state
= stateInitial
283 func (conn
*connection
) resetBuffers() {
285 conn
.rcptTo
= make([]mail
.Address
, 0)