SMTP支持LOGIN登陆方式
允许向不安全服务端发送邮件
bug fix
This commit is contained in:
jinnrry 2024-01-28 11:09:25 +08:00
parent 831c1f45ac
commit f5b63355b9
9 changed files with 105 additions and 23 deletions

View File

@ -61,10 +61,10 @@ First go to [spamhaus](https://check.spamhaus.org/) and check your domain name a
Or
`docker run -p 25:25 -p 80:80 -p 443:443 -p 110:110 -p 465:465 -v $(pwd)/config:/work/config ghcr.io/jinnrry/pmail:latest`
`docker run -p 25:25 -p 80:80 -p 443:443 -p 110:110 -p 465:465 -p 995:995 -v $(pwd)/config:/work/config ghcr.io/jinnrry/pmail:latest`
> [!IMPORTANT]
> If your server has a firewall turned on, you need to open ports 25, 80, 110, 443, 465
> If your server has a firewall turned on, you need to open ports 25, 80, 110, 443, 465, 995
## 3、Configuration

View File

@ -65,10 +65,10 @@ PMail是一个追求极简部署流程、极致资源占用的个人域名邮箱
或者
`docker run -p 25:25 -p 80:80 -p 443:443 -p 110:110 -p 465:465 -v $(pwd)/config:/work/config ghcr.io/jinnrry/pmail:latest`
`docker run -p 25:25 -p 80:80 -p 443:443 -p 110:110 -p 465:465 -p 995:995 -v $(pwd)/config:/work/config ghcr.io/jinnrry/pmail:latest`
> [!IMPORTANT]
> 如果你服务器开启了防火墙,你需要放行25、80、110、443、465这五个端口
> 如果你服务器开启了防火墙,你需要打开25、80、110、443、465、995端口
## 3、配置

View File

@ -39,7 +39,7 @@ type Config struct {
//go:embed tables/*
var tableConfig embed.FS
const Version = "2.3.6"
const Version = "2.3.7"
const DBTypeMySQL = "mysql"
const DBTypeSQLite = "sqlite"

View File

@ -5,7 +5,7 @@ go 1.21
require (
github.com/Jinnrry/gopop v0.0.0-20231113115125-fbdf52ae39ea
github.com/alexedwards/scs/mysqlstore v0.0.0-20230327161757-10d4299e3b24
github.com/alexedwards/scs/sqlite3store v0.0.0-20230327161757-10d4299e3b24
github.com/alexedwards/scs/sqlite3store v0.0.0-20231113091146-cef4b05350c8
github.com/alexedwards/scs/v2 v2.5.1
github.com/emersion/go-message v0.18.0
github.com/emersion/go-msgauth v0.6.6
@ -21,7 +21,7 @@ require (
modernc.org/sqlite v1.24.0
)
replace github.com/alexedwards/scs/sqlite3store v0.0.0-20230327161757-10d4299e3b24 => github.com/Jinnrry/scs/sqlite3store v0.0.0-20230803080525-914f01e0d379
replace github.com/emersion/go-smtp v0.20.1 => github.com/jinnrry/go-smtp v0.20.1
require (
github.com/cenkalti/backoff/v4 v4.2.1 // indirect

View File

@ -4,6 +4,9 @@ github.com/Jinnrry/scs/sqlite3store v0.0.0-20230803080525-914f01e0d379 h1:i6LB/3
github.com/Jinnrry/scs/sqlite3store v0.0.0-20230803080525-914f01e0d379/go.mod h1:Iyk7S76cxGaiEX/mSYmTZzYehp4KfyylcLaV3OnToss=
github.com/alexedwards/scs/mysqlstore v0.0.0-20230327161757-10d4299e3b24 h1:1jXpX7IE/zuf9FZQJpqZNepXqW8mq6NLzplHDCA43HY=
github.com/alexedwards/scs/mysqlstore v0.0.0-20230327161757-10d4299e3b24/go.mod h1:ShejCOaSJCEjCWjc7YBrgy2xd0Kp+wiyBdzTNQrAGn4=
github.com/alexedwards/scs/sqlite3store v0.0.0-20230327161757-10d4299e3b24/go.mod h1:Iyk7S76cxGaiEX/mSYmTZzYehp4KfyylcLaV3OnToss=
github.com/alexedwards/scs/sqlite3store v0.0.0-20231113091146-cef4b05350c8 h1:mnXnnXEjn8QIyv4KCN0+IjDlXA64qdq2hIVOmfNFeuY=
github.com/alexedwards/scs/sqlite3store v0.0.0-20231113091146-cef4b05350c8/go.mod h1:Iyk7S76cxGaiEX/mSYmTZzYehp4KfyylcLaV3OnToss=
github.com/alexedwards/scs/v2 v2.5.1 h1:EhAz3Kb3OSQzD8T+Ub23fKsiuvE0GzbF5Lgn0uTwM3Y=
github.com/alexedwards/scs/v2 v2.5.1/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
@ -22,8 +25,6 @@ github.com/emersion/go-msgauth v0.6.6 h1:buv5lL8v/3v4RpHnQFS2IPhE3nxSRX+AxnrEJbD
github.com/emersion/go-msgauth v0.6.6/go.mod h1:A+/zaz9bzukLM6tRWRgJ3BdrBi+TFKTvQ3fGMFOI9SM=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.20.1 h1:kW8Nkzomjk6W1ebfUYy6wAYThukCzD9MFOkFnragOHc=
github.com/emersion/go-smtp v0.20.1/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
@ -44,6 +45,8 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbu
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jinnrry/go-smtp v0.20.1 h1:coy2+Ch7+ptr3J4rHwE+YVY25eaXuUbi4PNHchfc6ro=
github.com/jinnrry/go-smtp v0.20.1/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=

View File

@ -110,7 +110,7 @@ func StartWithTLS() {
instanceTls.MaxMessageBytes = 1024 * 1024
instanceTls.MaxRecipients = 50
// force TLS for auth
instanceTls.AllowInsecureAuth = false
instanceTls.AllowInsecureAuth = true
// Load the certificate and key
cer, err := tls.LoadX509KeyPair(config.Instance.SSLPublicKeyPath, config.Instance.SSLPrivateKeyPath)
if err != nil {

View File

@ -154,25 +154,24 @@ func Send(ctx *context.Context, e *parsemail.Email) (error, map[string]error) {
as.WaitProcess(func(p any) {
err := smtp.SendMail("", domain.mxHost+":25", nil, e.From.EmailAddress, buildAddress(tos), b)
if err != nil {
log.WithContext(ctx).Warnf("SMTP Send Error! Error:%+v", err)
} else {
log.WithContext(ctx).Infof("SMTP Send Success !")
}
// 重新选取证书域名
// 使用其他方式发送
if err != nil {
if errors.Is(err, smtp.NoSupportSTARTTLSError) {
err = smtp.SendMailWithTls("", domain.mxHost+":465", nil, e.From.EmailAddress, buildAddress(tos), b)
if err != nil {
log.WithContext(ctx).Warnf("Unsafe! %s Server Not Support SMTPS & STARTTLS", domain.domain)
err = smtp.SendMailUnsafe("", domain.mxHost+":25", nil, e.From.EmailAddress, buildAddress(tos), b)
}
}
// 证书错误,从新选取证书发送
if certificateErr, ok := err.(*tls.CertificateVerificationError); ok {
if hostnameErr, is := certificateErr.Err.(x509.HostnameError); is {
if hostnameErr.Certificate != nil {
certificateHostName := hostnameErr.Certificate.DNSNames
// smtps发送失败,尝试smtp
// 重新选取证书发送
err = smtp.SendMail(domainMatch(domain.domain, certificateHostName), domain.mxHost+":25", nil, e.From.EmailAddress, buildAddress(tos), b)
if err != nil {
log.WithContext(ctx).Warnf("SMTP Send Error! Error:%+v", err)
} else {
log.WithContext(ctx).Infof("SMTP Send Success !")
}
}
}
}
@ -208,6 +207,10 @@ func buildAddress(u []*parsemail.User) []string {
}
func domainMatch(domain string, dnsNames []string) string {
if len(dnsNames) == 0 {
return domain
}
secondMatch := ""
for _, name := range dnsNames {

View File

@ -1,6 +1,7 @@
package send
import (
"fmt"
log "github.com/sirupsen/logrus"
"os"
"pmail/config"
@ -49,3 +50,26 @@ func TestSend(t *testing.T) {
}
Send(nil, e)
}
func TestSendSohu(t *testing.T) {
testInit()
e := &parsemail.Email{
From: &parsemail.User{
Name: "发送人",
EmailAddress: "j@jinnrry.com",
},
To: []*parsemail.User{
{"jinnrry@sohu.com", "名"},
},
Subject: "插件测试",
Text: []byte("这是Text"),
HTML: []byte("<div>这是Html</div>"),
}
Send(nil, e)
}
func Test_domainMatch(t *testing.T) {
domain := domainMatch("qq.com", nil)
fmt.Println(domain)
}

View File

@ -33,6 +33,8 @@ import (
"time"
)
var NoSupportSTARTTLSError = errors.New("smtp: server doesn't support STARTTLS")
// A Client represents a client connection to an SMTP server.
type Client struct {
// Text is the textproto.Conn used by the Client. It is exported to allow for
@ -417,7 +419,7 @@ func SendMail(domain string, addr string, a smtp.Auth, from string, to []string,
return err
}
if ok, _ := c.Extension("STARTTLS"); !ok {
return errors.New("smtp: server doesn't support STARTTLS")
return NoSupportSTARTTLSError
}
var config *tls.Config
@ -461,6 +463,56 @@ func SendMail(domain string, addr string, a smtp.Auth, from string, to []string,
return c.Quit()
}
// SendMailUnsafe 无TLS加密的邮件发送方式
func SendMailUnsafe(domain string, addr string, a smtp.Auth, from string, to []string, msg []byte) error {
if err := validateLine(from); err != nil {
return err
}
for _, recp := range to {
if err := validateLine(recp); err != nil {
return err
}
}
c, err := Dial(addr)
if err != nil {
return err
}
defer c.Close()
if err = c.hello(); err != nil {
return err
}
if a != nil && c.ext != nil {
if _, ok := c.ext["AUTH"]; !ok {
return errors.New("smtp: server doesn't support AUTH")
}
if err = c.Auth(a); err != nil {
return err
}
}
if err = c.Mail(from); err != nil {
return err
}
for _, addr := range to {
if err = c.Rcpt(addr); err != nil {
return err
}
}
w, err := c.Data()
if err != nil {
return err
}
_, err = w.Write(msg)
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
return c.Quit()
}
// Extension reports whether an extension is support by the server.
// The extension name is case-insensitive. If the extension is supported,
// Extension also returns a string that contains any parameters the