smtp: add debug option

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2023-05-30 19:11:28 +02:00
parent a2fc7d3cc5
commit 8339fee69d
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
10 changed files with 101 additions and 17 deletions

View file

@ -457,6 +457,7 @@ The configuration file contains the following sections:
- `encryption`, integer. 0 means no encryption, 1 means `TLS`, 2 means `STARTTLS`. Default: `0`. - `encryption`, integer. 0 means no encryption, 1 means `TLS`, 2 means `STARTTLS`. Default: `0`.
- `domain`, string. Domain to use for `HELO` command, if empty `localhost` will be used. Default: blank. - `domain`, string. Domain to use for `HELO` command, if empty `localhost` will be used. Default: blank.
- `templates_path`, string. Path to the email templates. This can be an absolute path or a path relative to the config dir. Templates are searched within a subdirectory named "email" in the specified path. You can customize the email templates by simply specifying an alternate path and putting your custom templates there. - `templates_path`, string. Path to the email templates. This can be an absolute path or a path relative to the config dir. Templates are searched within a subdirectory named "email" in the specified path. You can customize the email templates by simply specifying an alternate path and putting your custom templates there.
- `debug`, integer. Set to `1` to enable SMTP debug. Default: `0`.
</details> </details>
<details><summary><font size=4>Plugins</font></summary> <details><summary><font size=4>Plugins</font></summary>

10
go.mod
View file

@ -25,7 +25,7 @@ require (
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001
github.com/fclairamb/ftpserverlib v0.21.0 github.com/fclairamb/ftpserverlib v0.21.0
github.com/fclairamb/go-log v0.4.1 github.com/fclairamb/go-log v0.4.1
github.com/go-acme/lego/v4 v4.11.0 github.com/go-acme/lego/v4 v4.12.0
github.com/go-chi/chi/v5 v5.0.8 github.com/go-chi/chi/v5 v5.0.8
github.com/go-chi/jwtauth/v5 v5.1.0 github.com/go-chi/jwtauth/v5 v5.1.0
github.com/go-chi/render v1.0.2 github.com/go-chi/render v1.0.2
@ -145,7 +145,7 @@ require (
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/spf13/cast v1.5.1 // indirect github.com/spf13/cast v1.5.1 // indirect
@ -160,9 +160,9 @@ require (
golang.org/x/tools v0.9.1 // indirect golang.org/x/tools v0.9.1 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230526015343-6ee61e4f9d5f // indirect google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230526015343-6ee61e4f9d5f // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526015343-6ee61e4f9d5f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e // indirect
google.golang.org/grpc v1.55.0 // indirect google.golang.org/grpc v1.55.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect

20
go.sum
View file

@ -951,8 +951,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/go-acme/lego/v4 v4.11.0 h1:oIPoU7zBJoTfoVrbqk62+/2NsGCSgCVK1JtZSZZ28SU= github.com/go-acme/lego/v4 v4.12.0 h1:jox3II6YRjt1EXvrymSQuSNgEUOcbUkF2je0kyuv6YM=
github.com/go-acme/lego/v4 v4.11.0/go.mod h1:dENL0J3/WughN2NLy0T35otK5k1EWCmXTwCw0+X5ZaE= github.com/go-acme/lego/v4 v4.12.0/go.mod h1:UZoOlhVmUYP/N0z4tEbfUjoCNHRZNObzqWZtT76DIsc=
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/jwtauth/v5 v5.1.0 h1:wJyf2YZ/ohPvNJBwPOzZaQbyzwgMZZceE1m8FOzXLeA= github.com/go-chi/jwtauth/v5 v5.1.0 h1:wJyf2YZ/ohPvNJBwPOzZaQbyzwgMZZceE1m8FOzXLeA=
@ -1799,8 +1799,8 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/prometheus/procfs v0.10.0 h1:UkG7GPYkO4UZyLnyXjaWYcgOSONqwdBqFUT95ugmt6I= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY= github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY=
github.com/prometheus/prometheus v0.42.0/go.mod h1:Pfqb/MLnnR2KK+0vchiaH39jXxvLMBk+3lnIGP4N7Vk= github.com/prometheus/prometheus v0.42.0/go.mod h1:Pfqb/MLnnR2KK+0vchiaH39jXxvLMBk+3lnIGP4N7Vk=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
@ -2816,12 +2816,12 @@ google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ
google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230526015343-6ee61e4f9d5f h1:DwRdHa3+SynqBR2tx3LVtzJrGooL9hg1OCAfBdQAk1A= google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e h1:Ao9GzfUMPH3zjVfzXG5rlWlk+Q8MXWKwWpwVQE1MXfw=
google.golang.org/genproto v0.0.0-20230526015343-6ee61e4f9d5f/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
google.golang.org/genproto/googleapis/api v0.0.0-20230526015343-6ee61e4f9d5f h1:dJhNU2ZodW2tHjMhmDOrcRSahqR0wgfOEBs8nSmVx5Y= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e h1:AZX1ra8YbFMSb7+1pI8S9v4rrgRR7jU1FmuFSSjTVcQ=
google.golang.org/genproto/googleapis/api v0.0.0-20230526015343-6ee61e4f9d5f/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526015343-6ee61e4f9d5f h1:QNVuVEP2S7NNxLdNdOq0RiW3c9pW4gIpUUd+GAOjk1Y= google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e h1:NumxXLPfHSndr3wBBdeKiVHjGVFzi9RX2HwwQke94iY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526015343-6ee61e4f9d5f/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=

View file

@ -50,6 +50,7 @@ If the SMTP configuration is correct you should receive this email.`,
os.Exit(1) os.Exit(1)
} }
smtpConfig := config.GetSMTPConfig() smtpConfig := config.GetSMTPConfig()
smtpConfig.Debug = 1
err = smtpConfig.Initialize(configDir, false) err = smtpConfig.Initialize(configDir, false)
if err != nil { if err != nil {
logger.ErrorToConsole("unable to initialize SMTP configuration: %v", err) logger.ErrorToConsole("unable to initialize SMTP configuration: %v", err)

View file

@ -152,6 +152,7 @@ type SMTPConfigs struct {
AuthType int `json:"auth_type,omitempty"` AuthType int `json:"auth_type,omitempty"`
Encryption int `json:"encryption,omitempty"` Encryption int `json:"encryption,omitempty"`
Domain string `json:"domain,omitempty"` Domain string `json:"domain,omitempty"`
Debug int `json:"debug,omitempty"`
} }
func (c *SMTPConfigs) isEmpty() bool { func (c *SMTPConfigs) isEmpty() bool {
@ -215,6 +216,7 @@ func (c *SMTPConfigs) getACopy() *SMTPConfigs {
AuthType: c.AuthType, AuthType: c.AuthType,
Encryption: c.Encryption, Encryption: c.Encryption,
Domain: c.Domain, Domain: c.Domain,
Debug: c.Debug,
} }
} }

View file

@ -2635,6 +2635,10 @@ func getSMTPConfigsFromPostFields(r *http.Request) *dataprovider.SMTPConfigs {
if err != nil { if err != nil {
encryption = 0 encryption = 0
} }
debug := 0
if r.Form.Get("smtp_debug") != "" {
debug = 1
}
return &dataprovider.SMTPConfigs{ return &dataprovider.SMTPConfigs{
Host: r.Form.Get("smtp_host"), Host: r.Form.Get("smtp_host"),
Port: port, Port: port,
@ -2644,6 +2648,7 @@ func getSMTPConfigsFromPostFields(r *http.Request) *dataprovider.SMTPConfigs {
AuthType: authType, AuthType: authType,
Encryption: encryption, Encryption: encryption,
Domain: r.Form.Get("smtp_domain"), Domain: r.Form.Get("smtp_domain"),
Debug: debug,
} }
} }

48
internal/logger/mail.go Normal file
View file

@ -0,0 +1,48 @@
// Copyright (C) 2019-2023 Nicola Murino
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package logger
const (
mailLogSender = "smtpclient"
)
// MailAdapter is an adapter for mail.Logger
type MailAdapter struct {
ConnectionID string
}
// Errorf emits a log at Error level
func (l *MailAdapter) Errorf(format string, v ...any) {
ErrorToConsole(format, v...)
Log(LevelError, mailLogSender, l.ConnectionID, format, v...)
}
// Warnf emits a log at Warn level
func (l *MailAdapter) Warnf(format string, v ...any) {
WarnToConsole(format, v...)
Log(LevelWarn, mailLogSender, l.ConnectionID, format, v...)
}
// Infof emits a log at Info level
func (l *MailAdapter) Infof(format string, v ...any) {
InfoToConsole(format, v...)
Log(LevelInfo, mailLogSender, l.ConnectionID, format, v...)
}
// Debugf emits a log at Debug level
func (l *MailAdapter) Debugf(format string, v ...any) {
DebugToConsole(format, v...)
Log(LevelDebug, mailLogSender, l.ConnectionID, format, v...)
}

View file

@ -25,6 +25,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/rs/xid"
"github.com/wneessen/go-mail" "github.com/wneessen/go-mail"
"github.com/drakkan/sftpgo/v2/internal/dataprovider" "github.com/drakkan/sftpgo/v2/internal/dataprovider"
@ -83,6 +84,7 @@ func (c *activeConfig) Set(cfg *dataprovider.SMTPConfigs) {
AuthType: cfg.AuthType, AuthType: cfg.AuthType,
Encryption: cfg.Encryption, Encryption: cfg.Encryption,
Domain: cfg.Domain, Domain: cfg.Domain,
Debug: cfg.Debug,
} }
} }
@ -167,6 +169,8 @@ type Config struct {
// Path to the email templates. This can be an absolute path or a path relative to the config dir. // Path to the email templates. This can be an absolute path or a path relative to the config dir.
// Templates are searched within a subdirectory named "email" in the specified path // Templates are searched within a subdirectory named "email" in the specified path
TemplatesPath string `json:"templates_path" mapstructure:"templates_path"` TemplatesPath string `json:"templates_path" mapstructure:"templates_path"`
// Set to 1 to enable debug logs
Debug int `json:"debug" mapstructure:"debug"`
} }
func (c *Config) isEqual(other *Config) bool { func (c *Config) isEqual(other *Config) bool {
@ -194,6 +198,9 @@ func (c *Config) isEqual(other *Config) bool {
if c.Domain != other.Domain { if c.Domain != other.Domain {
return false return false
} }
if c.Debug != other.Debug {
return false
}
return true return true
} }
@ -283,6 +290,13 @@ func (c *Config) getMailClientOptions() []mail.Option {
if c.Domain != "" { if c.Domain != "" {
options = append(options, mail.WithHELO(c.Domain)) options = append(options, mail.WithHELO(c.Domain))
} }
if c.Debug > 0 {
options = append(options,
mail.WithLogger(&logger.MailAdapter{
ConnectionID: xid.New().String(),
}),
mail.WithDebugLog())
}
return options return options
} }

View file

@ -406,7 +406,8 @@
"auth_type": 0, "auth_type": 0,
"encryption": 0, "encryption": 0,
"domain": "", "domain": "",
"templates_path": "templates" "templates_path": "templates",
"debug": 0
}, },
"plugins": [] "plugins": []
} }

View file

@ -275,6 +275,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</div> </div>
</div> </div>
<div class="form-group">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="idSMTPDebug" name="smtp_debug"
{{if gt .Configs.SMTP.Debug 0}}checked{{end}}>
<label for="idSMTPDebug" class="form-check-label">Debug logs</label>
</div>
</div>
<div class="form-group row"> <div class="form-group row">
<div class="col-sm-12"> <div class="col-sm-12">
<div class="input-group"> <div class="input-group">
@ -352,6 +360,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
$('#smtpTestResultModal').modal('show'); $('#smtpTestResultModal').modal('show');
return; return;
} }
let debug = 0;
if ($('#idSMTPDebug').is(':checked')){
debug = 1;
}
$('#smtpSuccessMsg').hide(); $('#smtpSuccessMsg').hide();
$('#smtpErrorMsg').hide(); $('#smtpErrorMsg').hide();
showSpinner(); showSpinner();
@ -360,7 +372,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
url: "{{.ConfigsURL}}/smtp/test", url: "{{.ConfigsURL}}/smtp/test",
type: 'POST', type: 'POST',
headers: {'X-CSRF-TOKEN' : '{{.CSRFToken}}'}, headers: {'X-CSRF-TOKEN' : '{{.CSRFToken}}'},
data: JSON.stringify({"host": $('#idSMTPHost').val(),"port": parseInt($('#idSMTPPort').val()),"from": $('#idSMTPFrom').val(),"user": $('#idSMTPUsername').val(),"password": $('#idSMTPPassword').val(),"auth_type": parseInt($('#idSMTPAuth').val()),"encryption": parseInt($('#idSMTPEncryption').val()), "domain": $('#idSMTPDomain').val(),"recipient": recipient}), data: JSON.stringify({"host": $('#idSMTPHost').val(),"port": parseInt($('#idSMTPPort').val()),"from": $('#idSMTPFrom').val(),"user": $('#idSMTPUsername').val(),"password": $('#idSMTPPassword').val(),"auth_type": parseInt($('#idSMTPAuth').val()),"encryption": parseInt($('#idSMTPEncryption').val()), "domain": $('#idSMTPDomain').val(),"debug": debug, "recipient": recipient}),
dataType: 'json', dataType: 'json',
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
timeout: 15000, timeout: 15000,