]>
src.bluestatic.org Git - mailpopbox.git/blob - smtp/conn.go
17 stateNew state
= iota // Before EHLO.
24 type connection
struct {
39 mailFrom
*mail
.Address
43 func AcceptConnection(netConn net
.Conn
, server Server
) error
{
46 tp
: textproto
.NewConn(netConn
),
48 remoteAddr
: netConn
.RemoteAddr(),
54 conn
.writeReply(220, fmt
.Sprintf("%s ESMTP [%s] (mailpopbox)", server
.Name(), netConn
.LocalAddr()))
57 conn
.line
, err
= conn
.tp
.ReadLine()
59 conn
.writeReply(500, "line too long")
64 if _
, err
= fmt
.Sscanf(conn
.line
, "%s", &cmd
); err
!= nil {
65 conn
.reply(ReplyBadSyntax
)
69 switch strings
.ToUpper(cmd
) {
71 conn
.writeReply(221, "Goodbye")
91 conn
.writeReply(252, "I'll do my best")
93 conn
.writeReply(550, "access denied")
97 conn
.writeReply(250, "https://tools.ietf.org/html/rfc5321")
99 conn
.writeReply(500, "unrecognized command")
106 func (conn
*connection
) reply(reply ReplyLine
) error
{
107 return conn
.writeReply(reply
.Code
, reply
.Message
)
110 func (conn
*connection
) writeReply(code
int, msg
string) error
{
112 return conn
.tp
.PrintfLine("%d %s", code
, msg
)
114 return conn
.tp
.PrintfLine("%d", code
)
118 // parsePath parses out either a forward-, reverse-, or return-path from the
119 // current connection line. Returns a (valid-path, ReplyOK) if it was
120 // successfully parsed.
121 func (conn
*connection
) parsePath(command
string) (string, ReplyLine
) {
122 if len(conn
.line
) < len(command
) {
123 return "", ReplyBadSyntax
125 if strings
.ToUpper(command
) != strings
.ToUpper(conn
.line
[:len(command
)]) {
126 return "", ReplyLine
{500, "unrecognized command"}
128 return conn
.line
[len(command
):], ReplyOK
131 func (conn
*connection
) doEHLO() {
135 _
, err
:= fmt
.Sscanf(conn
.line
, "%s %s", &cmd
, &conn
.ehlo
)
137 conn
.reply(ReplyBadSyntax
)
142 conn
.writeReply(250, fmt
.Sprintf("Hello %s [%s]", conn
.ehlo
, conn
.remoteAddr
))
144 conn
.tp
.PrintfLine("250-Hello %s [%s]", conn
.ehlo
, conn
.remoteAddr
)
145 if conn
.server
.TLSConfig() != nil && conn
.tlsNc
== nil {
146 conn
.tp
.PrintfLine("250-STARTTLS")
148 conn
.tp
.PrintfLine("250 SIZE %d", 40960000)
151 conn
.state
= stateInitial
154 func (conn
*connection
) doSTARTTLS() {
155 if conn
.state
!= stateInitial
{
156 conn
.reply(ReplyBadSequence
)
160 tlsConfig
:= conn
.server
.TLSConfig()
161 if !conn
.esmtp || tlsConfig
== nil {
162 conn
.writeReply(500, "unrecognized command")
166 conn
.writeReply(220, "initiate TLS connection")
168 newConn
:= tls
.Server(conn
.nc
, tlsConfig
)
169 if err
:= newConn
.Handshake(); err
!= nil {
174 conn
.tp
= textproto
.NewConn(conn
.tlsNc
)
175 conn
.state
= stateInitial
177 conn
.writeReply(220, fmt
.Sprintf("%s ESMTPS [%s] (mailpopbox)",
178 conn
.server
.Name(), newConn
.LocalAddr()))
181 func (conn
*connection
) doMAIL() {
182 if conn
.state
!= stateInitial
{
183 conn
.reply(ReplyBadSequence
)
187 mailFrom
, reply
:= conn
.parsePath("MAIL FROM:")
188 if reply
!= ReplyOK
{
194 conn
.mailFrom
, err
= mail
.ParseAddress(mailFrom
)
196 conn
.reply(ReplyBadSyntax
)
200 conn
.state
= stateMail
204 func (conn
*connection
) doRCPT() {
205 if conn
.state
!= stateMail
&& conn
.state
!= stateRecipient
{
206 conn
.reply(ReplyBadSequence
)
210 rcptTo
, reply
:= conn
.parsePath("RCPT TO:")
211 if reply
!= ReplyOK
{
216 address
, err
:= mail
.ParseAddress(rcptTo
)
218 conn
.reply(ReplyBadSyntax
)
221 if reply
:= conn
.server
.VerifyAddress(*address
); reply
!= ReplyOK
{
226 conn
.rcptTo
= append(conn
.rcptTo
, *address
)
228 conn
.state
= stateRecipient
232 func (conn
*connection
) doDATA() {
233 if conn
.state
!= stateRecipient
{
234 conn
.reply(ReplyBadSequence
)
238 conn
.writeReply(354, "Start mail input; end with <CRLF>.<CRLF>")
240 data
, err
:= conn
.tp
.ReadDotBytes()
243 conn
.writeReply(552, "transaction failed")
247 received
:= time
.Now()
249 RemoteAddr
: conn
.remoteAddr
,
251 MailFrom
: *conn
.mailFrom
,
254 ID
: conn
.envelopeID(received
),
257 trace
:= conn
.getReceivedInfo(env
)
259 env
.Data
= append(trace
, data
...)
261 if reply
:= conn
.server
.OnMessageDelivered(env
); reply
!= nil {
266 conn
.state
= stateInitial
270 func (conn
*connection
) envelopeID(t time
.Time
) string {
272 rand
.Read(idBytes
[:])
273 return fmt
.Sprintf("m.%d.%x", t
.UnixNano(), idBytes
)
276 func (conn
*connection
) getReceivedInfo(envelope Envelope
) []byte {
277 rhost
, _
, err
:= net
.SplitHostPort(conn
.remoteAddr
.String())
279 rhost
= conn
.remoteAddr
.String()
282 rhosts
, err
:= net
.LookupAddr(rhost
)
284 rhost
= fmt
.Sprintf("%s [%s]", rhosts
[0], rhost
)
287 base
:= fmt
.Sprintf("Received: from %s (%s)\r\n ", conn
.ehlo
, rhost
)
293 if conn
.tlsNc
!= nil {
296 base
+= fmt
.Sprintf("by %s (mailpopbox) with %s id %s\r\n ", conn
.server
.Name(), with
, envelope
.ID
)
298 base
+= fmt
.Sprintf("for <%s>\r\n ", envelope
.RcptTo
[0].Address
)
300 transport
:= "PLAINTEXT"
301 if conn
.tlsNc
!= nil {
302 // TODO: TLS version, cipher, bits
304 date
:= envelope
.Received
.Format(time
.RFC1123Z
) // Same as RFC 5322 ยง 3.3
305 base
+= fmt
.Sprintf("(using %s);\r\n %s\r\n", transport
, date
)
310 func (conn
*connection
) doRSET() {
311 conn
.state
= stateInitial
316 func (conn
*connection
) resetBuffers() {
318 conn
.rcptTo
= make([]mail
.Address
, 0)