emersion / go-smtp Goto Github PK
View Code? Open in Web Editor NEW📤 An SMTP client & server library written in Go
License: MIT License
📤 An SMTP client & server library written in Go
License: MIT License
Hi there,
Thanks for your work on go-imap and go-smtp! I'm attempting to use them for an email-related project, and while it seems I can use go-smtp to implement a message submission port well enough, I can't use it to accept email from the wider Internet (e.g., the kind of thing Exim usually does when configured as an internet site).
An SMTP transaction of this kind would generally look like:
EHLO example.com
MAIL FROM:...
RCPT TO:...
DATA
...
.
QUIT
Would you be willing to consider a PR that allows this behaviour to be optionally enabled? Something like:
be := mybackend.NewSubmission()
unauthenticatedBE := mybackend.NewSMTP()
server := smtp.NewServer(backend)
server.UnauthenticatedBackend = unauthenticatedBE
If the UnauthenticatedBackend
is nil, we can continue to reject as we do presently. If it's set, login is optional. If a message comes through without a login happening, the UnauthenticatedBackend
handles it.
We could also add:
server.LoginDisabled = true
This would disable the AUTH command completely, which would make things a bit cleaner from my side :)
Happy to do the work for this, I just wanted to open a line of communication about it before starting! Let me know.
We could check for returned errors more often :-)
client.go:225:9: Error return value of `c.Quit` is not checked
c.Quit()
client.go:251:9: Error return value of `c.cmd` is not checked
c.cmd(501, "*")
client.go:252:10: Error return value of `c.Quit` is not checked
c.Quit()
client_test.go:42:8: Error return value of `c.Auth` is not checked
c.Auth(toServerEmptyAuth{})
client_test.go:437:18: Error return value of `bcmdbuf.Write` is not checked
bcmdbuf.Write([]byte(msg + "\r\n"))
client_test.go:633:15: Error return value of `c.Quit` is not checked
defer c.Quit()
client_test.go:669:11: Error return value of `s.w.Write` is not checked
s.w.Write([]byte(f + "\r\n"))
conn.go:143:17: Error return value of `session.Logout` is not checked
session.Logout()
conn.go:439:9: Error return value of `io.Copy` is not checked
io.Copy(ioutil.Discard, r) // Make sure all the data has been consumed
conn.go:477:26: Error return value of `c.conn.SetWriteDeadline` is not checked
c.conn.SetWriteDeadline(time.Now().Add(c.server.WriteTimeout))
conn.go:502:19: Error return value of `c.session.Logout` is not checked
c.session.Logout()
server.go:91:18: Error return value of `s.handleConn` is not checked
go s.handleConn(newConn(c, s))
net/smtp
is frozen, copy it here so it can evolve.
There's some odd behavior happening with MaxMessageBytes being exceeded.
s := smtp.NewServer(be)
s.Addr = ":2345"
s.Domain = hostname
s.AuthDisabled = true
s.MaxMessageBytes = 2
log.Fatal(server.ListenAndServe())
When I exceed the maximum message size, four very peculiar things happen:
<CR><LF>.<CR><LF>
sequenceTrying ::1...
Connected to localhost.
Escape character is '^]'.
220 localhost ESMTP Service Ready
EHLO localhost
250-Hello localhost
250-PIPELINING
250-8BITMIME
250-ENHANCEDSTATUSCODES
250 SIZE 2
MAIL FROM: oogali@localhost
250 2.0.0 Roger, accepting mail from <oogali@localhost>
RCPT TO: test@localhost
250 2.0.0 I'll make sure <test@localhost> gets this
DATA
354 2.0.0 Go ahead. End your data with <CR><LF>.<CR><LF>
Hello.
250 2.0.0 OK: queued
QUIT
.
QUIT
221 2.0.0 Goodnight and good luck
Connection closed by foreign host.
$ gzcat ~/inbound-mail-queue/inboundfun.136391468.gz
From oogali@localhost Thu May 14 01:29:25 2020
Return-Path: <oogali@localhost>
X-Original-To: test@localhost
Delivered-To: test@localhost
Received: from localhost (localhost [IPv6:::1])
by localhost (inboundfun) with ESMTP id 8B1151CCC4
for <test@localhost>; Thu, 14 May 2020 01:29:25 +0000 (UTC)
Message-Id: <20200514012925.8B1151CCC4@localhost>
Date: Thu, 14 May 2020 01:29:25 +0000 (UTC)
From: <oogali@localhost>
He
It looks like the MAIL parsing in strict mode expects the only argument to be the FROM argument, but the SMTP spec allows for optional mail-parameters
that are associated with service extensions. See https://tools.ietf.org/html/rfc5321#section-3.3.
It might be safe to ignore these parameters, but right now the server returns a 501 because it expects the argument to end with a >
.
Example command from the client:
MAIL FROM:<[email protected]> AUTH=<username>
Response from go-smtp:
501 Was expecting MAIL arg syntax of FROM:<address>
It seems like KMail includes these additional parameters (see https://www.reddit.com/r/ProtonMail/comments/evadaz/need_help_setting_up_outgoing_with_kmail/), as well as emailrelay.
Unlike the From, Rcpt, and Data backend hooks, the Auth hook's error message gets ignored, and always replaced with a 454
.
Line 363 in 930d6df
It would be nice to have the same error reporting as the other hooks.
type Feature int
const (
FeatureSMTPUTF8 = 1<<iota
FeatureREQUIRETLS
FeatureBINARYMIME
)
// ...
type Backend interface {
// ...
Features() Feature
}
I'm get Message-ID from imap
mr, _ := mail.CreateReader(r)
header := mr.Header
msgId, _ = header.MessageID()
and build new message:
msg.SetHeader("In-Reply-To", msgId)
msg.SetHeader("References", msgId)
msg.SetHeader("From", login)
msg.SetHeader("To", "[email protected]")
This email is not displayed in the correspondence thread, it is visible as a new email. How to send email in thread?
I'm sorry if I am reading over something, but it seems a lot of this library is exactly the same as net/smtp
. What is the difference, and in which scenarios is net/smtp
not sufficient? Thanks :)
Client support: Prefer over DATA if available (avoids the overhead of dot-encoding), require if BODY=BINARYMIME is used (as required by spec).
Server support: Transparent support, io.Reader passed to Session.Data reads multiple chunks. Error returned by Data early consumes the current chunk and returns an error without consuming the whole message.
Support BODY=BINARYMIME argument for MAIL command, no other actions required.
Specification: https://tools.ietf.org/html/rfc3030
https://en.wikipedia.org/wiki/Local_Mail_Transfer_Protocol
LMTP is not much different from SMTP. The main differences are:
Some thoughts on adding server support for this.
The server code already supports the PLAIN method described in https://tools.ietf.org/html/rfc4954#section-4.1
however https://tools.ietf.org/html/rfc4422#section-3 is also relevant.
Client swaks
is liberal in using whichever PLAIN or LOGIN auth methods the server offers.
Some clients (e.g. Windows PowerShell Send-MailMessage
) cannot use PLAIN (according to tests I've tried).
Adding a sasl.Login
entry to func NewServer
in server.go
achieves most of what's needed. Then we interwork with clients such as swaks
.
This is because swaks
chooses to send the basic LOGIN
command form (without username) then await the server's challenge response.
Aforementioned Windows client chooses to always send the LOGIN base64username
form. This is not currently handled by go-sasl/login.go
func Next
, because it always begins in the loginNotStarted
state, ignoring the already provided username, and proceeds to challenge for it.
https://github.com/emersion/go-sasl/blob/7e096a0a6197b89989e8cc31016daa67c8c62051/login.go#L32
Ideally we support both LOGIN formats. I'm happy to do the work. I just had a question of style for you -
a) make go-sasl/login.go Next
be aware of the presence of an initial response
value, assume this is the username, and go straight to loginWaitingPassword
or
b) add conditional logic to
Line 359 in 469c269
Next
one time before entering the for
loop
b) limits scope of change to just this repo; but perhaps it's better to improve Next
to handle RFC4422 sec 3 fully.
See also
https://www.jetmore.org/john/code/swaks/latest/doc/ref.txt
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/send-mailmessage?view=powershell-6
I'd like to return custom responses in response to the execution of the Mail
, Rcpt
, and Data
functions of the Session object.
I see I can string together my own responses using SMTPError
, however the logic in the connection handling code doesn't particularly like receiving any sort of error.
Would it be against the spirit of the project if this logic was modified to further inspect SMTPError and only error out if SMTPError.Code was not 250?
I just copy the example codes , a server and a client and trying to send a test email.
the server outputs :
`
2020/01/29 17:30:45 Starting server at :1025
2020/01/29 17:31:19 Mail from: [email protected]
2020/01/29 17:31:19 Rcpt to: [email protected]
2020/01/29 17:31:19 Data: To: [email protected]
Subject: discount Gophers!
This is the email body.
`
but i just didn't receive any email.
would you please help me with that.
I've done some fuzzing and found one crash:
(parts of paths are replaced to xxxx)
goroutine 1 [running]:
github.com/emersion/go-smtp.(*Conn).handleAuth(0xc000366070, 0xc00036f8ed, 0x1)
/home/user/Documents/xxxx/src/github.com/emersion/go-smtp/conn.go:339 +0x97a
github.com/emersion/go-smtp.(*Conn).handle(0xc000366070, 0xc00036f8e8, 0x4, 0xc00036f8ed, 0x1)
/home/user/Documents/xxxx/src/github.com/emersion/go-smtp/conn.go:115 +0x180
github.com/emersion/go-smtp.(*Server).handleConn(0xc0000c38c0, 0xc000366070, 0x0, 0x0)
/home/user/Documents/xxxx/src/github.com/emersion/go-smtp/server.go:122 +0x287
github.com/emersion/go-smtp.(*Server).Serve(0xc0000c38c0, 0x649ac0, 0xc0000acc40, 0x0, 0x0)
/home/user/Documents/xxxx/src/github.com/emersion/go-smtp/server.go:93 +0xf2
fuzz/fuzzsmtpserver.Fuzz(0x7408aa0f7000, 0xd, 0x200000, 0x3)
/home/user/Documents/xxxx/src/fuzz/fuzzsmtpserver/fuzz.go:63 +0x262
go-fuzz-dep.Main(0xc000046f80, 0x1, 0x1)
/tmp/go-fuzz-build433312042/goroot/src/go-fuzz-dep/main.go:36 +0x1b6
main.main()
/tmp/go-fuzz-build433312042/gopath/src/fuzz/fuzzsmtpserver/go.fuzz.main/main.go:15 +0x52
exit status 2
After sending to socket following data
[user@work crashers]$ cat 82fbe7cec7da6c1559d3780b7c0122064c01f1f1 | hexdump
0000000 4845 4f4c 3020 410a 5455 2048 000b
000000d
[user@work crashers]$ cat 82fbe7cec7da6c1559d3780b7c0122064c01f1f1 | hexdump -c
0000000 E H L O 0 \n A U T H \v
000000d
[user@work crashers]$ cat 82fbe7cec7da6c1559d3780b7c0122064c01f1f1.quoted
"EHLO 0\nAUTH \v"
To perform fuzzing I've modified Serve function in Server, it looks like that now:
// Serve accepts incoming connections on the Listener l.
func (s *Server) Serve(l net.Listener) error {
s.listener = l
defer s.Close()
for {
c, err := l.Accept()
if err != nil {
return err
}
// Modified for fuzzing - removed go so its synchronous now
/* go */ s.handleConn(newConn(c, s))
}
}
I didn't test this on base(with go s.handleConn) version nevertheless it seems like it should work on original as well.
I guess I've run go get -u
before running it, someone might test that as well though.
Hello,
In classical inetd activation, inetd already "Accept"s the incoming connection from client, and then passes the accepted connection to the daemon.
In that case I need to bypass the Accept() call that happens in the Serve() method (server.go line 83).
Unfortunately the handleConn method is private, so I can't call it directly.
Hence my very small feature request: rname handleConn to HandleConn :)
Thanks,
Stephane
mail does not come to gmail
I think providing an interface similar to LMTPSession on the client side is a good solution.
Follow-up for #23.
Ref. foxcpp/maddy#205
Like Server.Debug but for Client.
Thank you for your contribution!
I want to use proxy to request, But I'm not found how to use proxy!
Thank You
RFC 5321:
4.5.3.2. Timeouts
An SMTP client MUST provide a timeout mechanism. It MUST use per-
command timeouts rather than somehow trying to time the entire mail
transaction. Timeouts SHOULD be easily reconfigurable, preferably
without recompiling the SMTP code.
hello, I would like to recommend that a minor release be made. From reading about the 2 commits besides the README tweaks since the last release, I see that there is both a race condition as well as a security issue fixed. Are there any other steps that need to be taken first? PR 72 also looks pretty critical to merge, though I see it shows a failed status due to a very small decrease in code coverage. It would be great to see that slip into the patch release.
how to solve this , thanks
package main
import (
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"strings"
"time"
"github.com/emersion/go-sasl"
"github.com/emersion/go-smtp"
)
type Backend struct{}
// Login handles a login command with username and password.
func (bkd *Backend) Login(state *smtp.ConnectionState, username, password string) (smtp.Session, error) {
if username != "username" || password != "password" {
return nil, errors.New("Invalid username or password")
}
return &Session{
to: make([]string, 0),
}, nil
}
// AnonymousLogin requires clients to authenticate using SMTP AUTH before sending emails
func (bkd *Backend) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
return nil, smtp.ErrAuthRequired
}
// A Session is returned after successful login.
type Session struct {
to []string
from string
data []byte
}
func (s *Session) Mail(from string, opts smtp.MailOptions) error {
s.from = from
log.Println("Mail from:", from)
return nil
}
func (s *Session) Rcpt(to string) error {
for _, v := range strings.Split(to, ",") {
t := strings.Trim(v, " ")
s.to = append(s.to, t)
}
log.Println("Rcpt to:", to)
return nil
}
func (s *Session) Data(r io.Reader) error {
b, err := ioutil.ReadAll(r)
if err != nil {
return err
}
log.Println("Data:", string(b))
for _, user := range s.to {
u := strings.Split(user, "@")
host := "smtp." + u[1] + ":465"
auth := sasl.NewPlainClient("", "", "")
err = smtp.SendMail(host, auth, s.from, []string{user}, r)
if err != nil {
fmt.Println(err)
return err
}
}
return nil
}
func (s *Session) Reset() {}
func (s *Session) Logout() error {
return nil
}
func main() {
be := &Backend{}
s := smtp.NewServer(be)
s.Addr = ":1025"
s.Domain = "smtp.hyahm.com"
s.ReadTimeout = 10 * time.Second
s.WriteTimeout = 10 * time.Second
s.MaxMessageBytes = 1024 * 1024
s.MaxRecipients = 50
s.AllowInsecureAuth = true
log.Println("Starting server at", s.Addr)
if err := s.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
#!/usr/bin/python3
import smtplib
from email.mime.text import MIMEText
from email.header import Header
# 第三方 SMTP 服务
mail_host="smtp.hyahm.com" #设置服务器
mail_user="username" #用户名
mail_pass="password" #口令
sender = '[email protected]'
receivers = ['[email protected]'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
message = MIMEText('Python 邮件发送测试...', 'plain', 'utf-8')
message['From'] = Header("菜鸟教程", 'utf-8')
message['To'] = Header("测试", 'utf-8')
subject = 'Python SMTP 邮件测试'
message['Subject'] = Header(subject, 'utf-8')
try:
smtpObj = smtplib.SMTP()
smtpObj.connect(mail_host, 1025) # 25 为 SMTP 端口号
smtpObj.login(mail_user,mail_pass)
smtpObj.sendmail(sender, receivers, message.as_string())
print ("邮件发送成功")
except smtplib.SMTPException as e:
print(e)
print ("Error: 无法发送邮件")
error message
(554, b'5.0.0 Error: transaction failed, blame it on the weather: EOF')
Can not receive mail
server.go
package main
import (
"errors"
"io"
"io/ioutil"
"log"
"time"
"github.com/emersion/go-smtp"
)
type Backend struct{}
// Login handles a login command with username and password.
func (bkd *Backend) Login(state *smtp.ConnectionState, username, password string) (smtp.Session, error) {
if username != "username" || password != "password" {
return nil, errors.New("Invalid username or password")
}
return &Session{}, nil
}
// AnonymousLogin requires clients to authenticate using SMTP AUTH before sending emails
func (bkd *Backend) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
return nil, smtp.ErrAuthRequired
}
// A Session is returned after successful login.
type Session struct{}
func (s *Session) Mail(from string, opts smtp.MailOptions) error {
log.Println("Mail from:", from)
return nil
}
func (s *Session) Rcpt(to string) error {
log.Println("Rcpt to:", to)
return nil
}
func (s *Session) Data(r io.Reader) error {
if b, err := ioutil.ReadAll(r); err != nil {
return err
} else {
log.Println("Data:", string(b))
}
return nil
}
func (s *Session) Reset() {}
func (s *Session) Logout() error {
return nil
}
func main() {
be := &Backend{}
s := smtp.NewServer(be)
s.Addr = ":1025"
s.Domain = "localhost"
s.ReadTimeout = 10 * time.Second
s.WriteTimeout = 10 * time.Second
s.MaxMessageBytes = 1024 * 1024
s.MaxRecipients = 50
s.AllowInsecureAuth = true
log.Println("Starting server at", s.Addr)
if err := s.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
client.py
#!/usr/bin/python3
import smtplib
from email.mime.text import MIMEText
from email.header import Header
# 第三方 SMTP 服务
mail_host="smtp.hyahm.com" #设置服务器
mail_user="username" #用户名
mail_pass="password" #口令
sender = '[email protected]'
receivers = ['[email protected]'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
message = MIMEText('Python 邮件发送测试...', 'plain', 'utf-8')
message['From'] = Header("菜鸟教程", 'utf-8')
message['To'] = Header("测试", 'utf-8')
subject = 'Python SMTP 邮件测试'
message['Subject'] = Header(subject, 'utf-8')
try:
smtpObj = smtplib.SMTP()
smtpObj.connect(mail_host, 1025) # 25 为 SMTP 端口号
smtpObj.login(mail_user,mail_pass)
smtpObj.sendmail(sender, receivers, message.as_string())
print ("邮件发送成功")
except smtplib.SMTPException as e:
print(e)
print ("Error: 无法发送邮件")
[root@cander test]# python testemail.py
邮件发送成功
server output
2020/01/06 15:23:44 Mail from: [email protected]
2020/01/06 15:23:44 Rcpt to: [email protected]
2020/01/06 15:23:44 Data: Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
From: =?utf-8?b?6I+c6bif5pWZ56iL?=
To: =?utf-8?b?5rWL6K+V?=
Subject: =?utf-8?b?UHl0aG9uIFNNVFAg6YKu5Lu25rWL6K+V?=
UHl0aG9uIOmCruS7tuWPkemAgea1i+ivlS4uLg==
Probably this library should have a panic handler in way similar to go-imap (emersion/go-imap#259). Wdyt?
How to send message with oauth access token?
How to send a message using ssl/tls?
Hi,
it would be useful to have the remote address of the connection to implement a filter based on sender IP.
I think it could be implemented defining User struct (UserData as User is already ad interface) with RemoteAddr field filled during init of connection.
This change will impact how User is defined as at this moment the Login functions are responsible of User creation. With this change the User object is created by the connection filling all relevant data and passing it to Login functions. Is this doable?
I believe most backend implementations could handle UTF-8 addresses and MIME headers just fine, so it is a good idea to tell clients that we are fine with UTF-8.
Also I think we should tell the backend that SMTPUTF8 support was requested (through MAIL argument). This way it can make decision on whether it can actually handle the message (say, if it implements transparent forwarding and upstream server doesn't support SMTPUTF8).
References:
Most SMTP servers I've used allow MAIL FROM: user@host
. I've never used one that required MAIL FROM:<user@host>
.
I note that the parsing for RCPT TO:
is far more forgiving - stripping the <>
chars from the trailing text.
Can I submit a PR to make this strict parsing an option?
.\sm.go:17:34: undefined: smtp.ConnectionState
.\sm.go:17:84: undefined: smtp.Session
.\sm.go:25:43: undefined: smtp.ConnectionState
.\sm.go:25:66: undefined: smtp.Session
.\sm.go:38:26: undefined: from
.\sm.go:64:3: s.ReadTimeout undefined (type *smtp.Server has no field or method ReadTimeout)
.\sm.go:65:3: s.WriteTimeout undefined (type *smtp.Server has no field or method WriteTimeout)
Brad's original https://github.com/bradfitz/go-smtpd was forked by https://github.com/mhale/smtpd who (with help) added a lot of RFC compliance and general updates. They don't have streaming support so I forked the project again and made https://github.com/Xeoncross/smtpd. I actually ended up rewriting a lot of it to remove code better handled by the stdlib.
I went ahead and benchmarked this repo against mine after realizing @emersion had basically made all the same design decisions (using textproto instead of manual string/byte checking) and it seems you have done a better job again.
Here you can see BenchmarkNetSMTP
(mine) against BenchmarkEmersionGoSMTP
(yours) with a basic 2k multipart/MIME email body read to ioutil.Discard
.
go test -bench=. --benchmem
goos: darwin
goarch: amd64
BenchmarkRawProcessingSequence-8 20000 63182 ns/op 34595 B/op 68 allocs/op
BenchmarkNetSMTP-8 2000 717042 ns/op 128394 B/op 338 allocs/op
BenchmarkEmersionGoSMTP-8 3000 517125 ns/op 68882 B/op 292 allocs/op
(Note: BenchmarkRawProcessingSequence is just a net.Pipe()
with no actual sockets or TCP overhead for comparison.)
Thanks again for your work on all these email libraries.
Such as Mailgun. They provide APIs that can be called via curl to deliver emails etc.
Would this project consider providing interfaces for user to integrate with APIs?
Adding context support would be nice for tracing and cancellation.
Thanks for this library!
Hi
Today when the message gets queued we send response 250 2.0.0 OK: queued
. Other smtp servers returns a messageId to help the client's track the request later, eg: google does 250 2.0.0 OK 1571601091 d44si1315748ede.149 - gsmtp
How to implement the same ?
I see this option in conn.go
func (c *Conn) handleData(arg string) {
....
code, enhancedCode, msg := toSMTPStatus(c.Session().Data(r))
Split this into two
msg, err := c.Session().Data(r)
code, enhancedCode, msg := toSMTPStatus(err, msg)
But this breaks the signature of Session
. What would you suggest in this? What are the other ways to achieve this?
e.g. success for recipient 1 and failure for recipient 2.
This is required for complete LMTP support. The client should also support this.
This could be done with a LMTPError
implementing error
.
We probably need to add a new type for transactions.
Replaced by Conn.TLSConnectionState
smtp server is only to send email?
if i want to have some users and password i must have some code with a mysql to manage the user?
There are one or two commits made upstream: https://github.com/golang/go/commits/master/src/net/smtp
DotReader does CRLF->LR conversion that is generally unwanted for most email processing needs.
Hello,
I'm doing some testing on this package, thanks for the great work. 👍
I wanted to share with you two possible denial of service.
Right now, anybody can open a connection and keep it for ever doing :
- Bad command ( commands that are not handle by the parse() function )
c.nbrErrors++ will be incremented but will never trigger a connection close()
- Speak Up (When command is empty)
A connection can stay open , sending empty commands for ever
In both cases, the attacker can open as many connection, send either "bad commands" not parsed. So , for example, 1 char, 2char, 3 char commands or just empty commands and will never reach any disconnect.
As a quick fix, I would suggest this to evaluate the c.nbrErrors counter and disconnect if > 3
In both scenarios
Example:
File: conn.go
Line: 101
if cmd == "" {
c.nbrErrors++
if c.nbrErrors > 3 {
c.WriteResponse(500, EnhancedCode{5, 5, 2}, "Too many errors")
c.Close()
}
c.WriteResponse(500, EnhancedCode{5, 5, 2}, "Speak up")
return
}
file: server.go
Line: 137
if err != nil {
c.nbrErrors++
if c.nbrErrors > 3 {
c.WriteResponse(500, EnhancedCode{5, 5, 2}, "Too many errors")
c.Close()
}
c.WriteResponse(501, EnhancedCode{5, 5, 2}, "Bad command")
continue
}
Hope this help.
#75 fixes part of the Close
logic, but we still need to make it so calling Close
twice doesn't panic with a double channel close.
Line 470 in 930d6df
Is it expected that every DATA phase always calls client reset? Or should this be just for certain circumstances. Commenting this line out seems to work fine via the client sending QUIT.
This may be helpful/relevant: https://stackoverflow.com/a/25023155/8545455
Disclosure: I haven't read the latest SMTP specs!
Basically, there is new argument to the MAIL FROM command (REQUIRETLS), it need to be parsed and passed to the backend. Also capability REQUIRETLS needs to be specified, but only when backend can actually make use of REQUIRETLS argument.
The message should be rejected immediately if REQUIRETLS argument is specified and the connection is not using TLS.
Related to foxcpp/maddy#123.
IETF I-D: https://tools.ietf.org/html/draft-ietf-uta-smtp-require-tls-09
Hi!
How do I read the SMTP server response on smtp.SendMail
? Example: my SMTP server tell me the queue id when the email is queued (250 2.0.0 Ok: queued as C0E5C82029\n
). I want to get this.
IT's possible?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.