Skip to content

Commit f2618e7

Browse files
committed
switch from go-simple-mail to go-mail
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
1 parent 6afbd77 commit f2618e7

File tree

8 files changed

+715
-277
lines changed

8 files changed

+715
-277
lines changed

go.mod

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ require (
1515
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.47
1616
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.0
1717
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.0
18-
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.0
18+
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.1
1919
github.com/aws/aws-sdk-go-v2/service/sts v1.18.0
2020
github.com/bmatcuk/doublestar/v4 v4.6.0
2121
github.com/cockroachdb/cockroach-go/v2 v2.2.20
@@ -59,27 +59,27 @@ require (
5959
github.com/spf13/viper v1.14.0
6060
github.com/stretchr/testify v1.8.1
6161
github.com/studio-b12/gowebdav v0.0.0-20221109171924-60ec5ad56012
62-
github.com/subosito/gotenv v1.4.1
62+
github.com/subosito/gotenv v1.4.2
6363
github.com/unrolled/secure v1.13.0
6464
github.com/wagslane/go-password-validator v0.3.0
65-
github.com/xhit/go-simple-mail/v2 v2.13.0
65+
github.com/wneessen/go-mail v0.3.8
6666
github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a
6767
go.etcd.io/bbolt v1.3.6
6868
go.uber.org/automaxprocs v1.5.1
69-
gocloud.dev v0.27.0
69+
gocloud.dev v0.28.0
7070
golang.org/x/crypto v0.5.0
7171
golang.org/x/net v0.5.0
7272
golang.org/x/oauth2 v0.4.0
7373
golang.org/x/sys v0.4.0
7474
golang.org/x/term v0.4.0
7575
golang.org/x/time v0.3.0
76-
google.golang.org/api v0.106.0
76+
google.golang.org/api v0.107.0
7777
gopkg.in/natefinch/lumberjack.v2 v2.0.0
7878
)
7979

8080
require (
8181
cloud.google.com/go v0.108.0 // indirect
82-
cloud.google.com/go/compute v1.15.0 // indirect
82+
cloud.google.com/go/compute v1.15.1 // indirect
8383
cloud.google.com/go/compute/metadata v0.2.3 // indirect
8484
cloud.google.com/go/iam v0.10.0 // indirect
8585
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
@@ -108,7 +108,6 @@ require (
108108
github.com/fsnotify/fsnotify v1.6.0 // indirect
109109
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
110110
github.com/go-ole/go-ole v1.2.6 // indirect
111-
github.com/go-test/deep v1.1.0 // indirect
112111
github.com/goccy/go-json v0.10.0 // indirect
113112
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
114113
github.com/golang/protobuf v1.5.2 // indirect
@@ -129,8 +128,7 @@ require (
129128
github.com/lestrrat-go/httprc v1.0.4 // indirect
130129
github.com/lestrrat-go/iter v1.0.2 // indirect
131130
github.com/lestrrat-go/option v1.0.1 // indirect
132-
github.com/lib/pq v1.10.7 // indirect
133-
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect
131+
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de // indirect
134132
github.com/magiconair/properties v1.8.7 // indirect
135133
github.com/mattn/go-colorable v0.1.13 // indirect
136134
github.com/mattn/go-isatty v0.0.17 // indirect
@@ -153,16 +151,15 @@ require (
153151
github.com/spf13/pflag v1.0.5 // indirect
154152
github.com/tklauser/go-sysconf v0.3.11 // indirect
155153
github.com/tklauser/numcpus v0.6.0 // indirect
156-
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect
157154
github.com/yusufpapurcu/wmi v1.2.2 // indirect
158155
go.opencensus.io v0.24.0 // indirect
159156
golang.org/x/mod v0.7.0 // indirect
160157
golang.org/x/text v0.6.0 // indirect
161158
golang.org/x/tools v0.5.0 // indirect
162159
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
163160
google.golang.org/appengine v1.6.7 // indirect
164-
google.golang.org/genproto v0.0.0-20230106154932-a12b697841d9 // indirect
165-
google.golang.org/grpc v1.51.0 // indirect
161+
google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5 // indirect
162+
google.golang.org/grpc v1.52.0 // indirect
166163
google.golang.org/protobuf v1.28.1 // indirect
167164
gopkg.in/ini.v1 v1.67.0 // indirect
168165
gopkg.in/yaml.v2 v2.4.0 // indirect

go.sum

Lines changed: 538 additions & 129 deletions
Large diffs are not rendered by default.

internal/common/dataretention.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
"sync"
3030
"time"
3131

32-
mail "github.com/xhit/go-simple-mail/v2"
32+
"github.com/wneessen/go-mail"
3333

3434
"github.com/drakkan/sftpgo/v2/internal/command"
3535
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
@@ -372,7 +372,7 @@ func (c *RetentionCheck) sendEmailNotification(errCheck error) error {
372372
Results: c.results,
373373
})
374374
}
375-
var files []mail.File
375+
var files []*mail.File
376376
f, err := params.getRetentionReportsAsMailAttachment()
377377
if err != nil {
378378
c.conn.Log(logger.LevelError, "unable to get retention report as mail attachment: %v", err)
@@ -391,11 +391,11 @@ func (c *RetentionCheck) sendEmailNotification(errCheck error) error {
391391
body := "Further details attached."
392392
err = smtp.SendEmail([]string{c.Email}, subject, body, smtp.EmailContentTypeTextPlain, files...)
393393
if err != nil {
394-
c.conn.Log(logger.LevelError, "unable to notify retention check result via email: %v, elapsed: %v", err,
394+
c.conn.Log(logger.LevelError, "unable to notify retention check result via email: %v, elapsed: %s", err,
395395
time.Since(startTime))
396396
return err
397397
}
398-
c.conn.Log(logger.LevelInfo, "retention check result successfully notified via email, elapsed: %v", time.Since(startTime))
398+
c.conn.Log(logger.LevelInfo, "retention check result successfully notified via email, elapsed: %s", time.Since(startTime))
399399
return nil
400400
}
401401

internal/common/dataretention_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ func TestRetentionValidation(t *testing.T) {
8484
smtpCfg := smtp.Config{
8585
Host: "mail.example.com",
8686
Port: 25,
87+
From: "notification@example.com",
8788
TemplatesPath: "templates",
8889
}
8990
err = smtpCfg.Initialize(configDir)
@@ -116,6 +117,7 @@ func TestRetentionEmailNotifications(t *testing.T) {
116117
smtpCfg := smtp.Config{
117118
Host: "127.0.0.1",
118119
Port: 2525,
120+
From: "notification@example.com",
119121
TemplatesPath: "templates",
120122
}
121123
err := smtpCfg.Initialize(configDir)

internal/common/eventmanager.go

Lines changed: 64 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import (
4141
"github.com/robfig/cron/v3"
4242
"github.com/rs/xid"
4343
"github.com/sftpgo/sdk"
44-
mail "github.com/xhit/go-simple-mail/v2"
44+
"github.com/wneessen/go-mail"
4545

4646
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
4747
"github.com/drakkan/sftpgo/v2/internal/logger"
@@ -563,45 +563,59 @@ func (p *EventParams) getCompressedDataRetentionReport() ([]byte, error) {
563563
return nil, errors.New("no data retention report available")
564564
}
565565
var b bytes.Buffer
566-
wr := zip.NewWriter(&b)
566+
if _, err := p.writeCompressedDataRetentionReports(&b); err != nil {
567+
return nil, err
568+
}
569+
return b.Bytes(), nil
570+
}
571+
572+
func (p *EventParams) writeCompressedDataRetentionReports(w io.Writer) (int64, error) {
573+
var n int64
574+
wr := zip.NewWriter(w)
575+
567576
for _, check := range p.retentionChecks {
568-
if size := int64(len(b.Bytes())); size > maxAttachmentsSize {
569-
eventManagerLog(logger.LevelError, "unable to get retention report, size too large: %s", util.ByteCountIEC(size))
570-
return nil, fmt.Errorf("unable to get retention report, size too large: %s", util.ByteCountIEC(size))
571-
}
572577
data, err := getCSVRetentionReport(check.Results)
573578
if err != nil {
574-
return nil, fmt.Errorf("unable to get CSV report: %w", err)
579+
return n, fmt.Errorf("unable to get CSV report: %w", err)
580+
}
581+
dataSize := int64(len(data))
582+
n += dataSize
583+
// we suppose a 3:1 compression ratio
584+
if n > (maxAttachmentsSize * 3) {
585+
eventManagerLog(logger.LevelError, "unable to get retention report, size too large: %s",
586+
util.ByteCountIEC(n))
587+
return n, fmt.Errorf("unable to get retention report, size too large: %s", util.ByteCountIEC(n))
575588
}
589+
576590
fh := &zip.FileHeader{
577591
Name: fmt.Sprintf("%s-%s.csv", check.ActionName, check.Username),
578592
Method: zip.Deflate,
579593
Modified: time.Now().UTC(),
580594
}
581595
f, err := wr.CreateHeader(fh)
582596
if err != nil {
583-
return nil, fmt.Errorf("unable to create zip header for file %q: %w", fh.Name, err)
597+
return n, fmt.Errorf("unable to create zip header for file %q: %w", fh.Name, err)
584598
}
585-
_, err = io.Copy(f, bytes.NewBuffer(data))
599+
_, err = io.CopyN(f, bytes.NewBuffer(data), dataSize)
586600
if err != nil {
587-
return nil, fmt.Errorf("unable to write content to zip file %q: %w", fh.Name, err)
601+
return n, fmt.Errorf("unable to write content to zip file %q: %w", fh.Name, err)
588602
}
589603
}
590604
if err := wr.Close(); err != nil {
591-
return nil, fmt.Errorf("unable to close zip writer: %w", err)
605+
return n, fmt.Errorf("unable to close zip writer: %w", err)
592606
}
593-
return b.Bytes(), nil
607+
return n, nil
594608
}
595609

596-
func (p *EventParams) getRetentionReportsAsMailAttachment() (mail.File, error) {
597-
var result mail.File
598-
data, err := p.getCompressedDataRetentionReport()
599-
if err != nil {
600-
return result, err
610+
func (p *EventParams) getRetentionReportsAsMailAttachment() (*mail.File, error) {
611+
if len(p.retentionChecks) == 0 {
612+
return nil, errors.New("no data retention report available")
601613
}
602-
result.Name = "retention-reports.zip"
603-
result.Data = data
604-
return result, nil
614+
return &mail.File{
615+
Name: "retention-reports.zip",
616+
Header: make(map[string][]string),
617+
Writer: p.writeCompressedDataRetentionReports,
618+
}, nil
605619
}
606620

607621
func (p *EventParams) getStringReplacements(addObjectData bool) []string {
@@ -905,34 +919,24 @@ func writeFileContent(conn *BaseConnection, virtualPath string, w io.Writer) err
905919
return err
906920
}
907921

908-
func getFileContent(conn *BaseConnection, virtualPath string, expectedSize int) ([]byte, error) {
909-
reader, cancelFn, err := getFileReader(conn, virtualPath)
910-
if err != nil {
911-
return nil, err
912-
}
922+
func getFileContentFn(conn *BaseConnection, virtualPath string, size int64) func(w io.Writer) (int64, error) {
923+
return func(w io.Writer) (int64, error) {
924+
reader, cancelFn, err := getFileReader(conn, virtualPath)
925+
if err != nil {
926+
return 0, err
927+
}
913928

914-
defer cancelFn()
915-
defer reader.Close()
929+
defer cancelFn()
930+
defer reader.Close()
916931

917-
data := make([]byte, expectedSize)
918-
_, err = io.ReadFull(reader, data)
919-
return data, err
932+
return io.CopyN(w, reader, size)
933+
}
920934
}
921935

922-
func getMailAttachments(user dataprovider.User, attachments []string, replacer *strings.Replacer) ([]mail.File, error) {
923-
var files []mail.File
924-
user, err := getUserForEventAction(user)
925-
if err != nil {
926-
return nil, err
927-
}
928-
connectionID := fmt.Sprintf("%s_%s", protocolEventAction, xid.New().String())
929-
err = user.CheckFsRoot(connectionID)
930-
defer user.CloseFs() //nolint:errcheck
931-
if err != nil {
932-
return nil, fmt.Errorf("error getting email attachments, unable to check root fs for user %q: %w", user.Username, err)
933-
}
934-
conn := NewBaseConnection(connectionID, protocolEventAction, "", "", user)
936+
func getMailAttachments(conn *BaseConnection, attachments []string, replacer *strings.Replacer) ([]*mail.File, error) {
937+
var files []*mail.File
935938
totalSize := int64(0)
939+
936940
for _, virtualPath := range replacePathsPlaceholders(attachments, replacer) {
937941
info, err := conn.DoStat(virtualPath, 0, false)
938942
if err != nil {
@@ -945,13 +949,10 @@ func getMailAttachments(user dataprovider.User, attachments []string, replacer *
945949
if totalSize > maxAttachmentsSize {
946950
return nil, fmt.Errorf("unable to send files as attachment, size too large: %s", util.ByteCountIEC(totalSize))
947951
}
948-
data, err := getFileContent(conn, virtualPath, int(info.Size()))
949-
if err != nil {
950-
return nil, fmt.Errorf("unable to get content for file %q, user %q: %w", virtualPath, conn.User.Username, err)
951-
}
952-
files = append(files, mail.File{
953-
Name: path.Base(virtualPath),
954-
Data: data,
952+
files = append(files, &mail.File{
953+
Name: path.Base(virtualPath),
954+
Header: make(map[string][]string),
955+
Writer: getFileContentFn(conn, virtualPath, info.Size()),
955956
})
956957
}
957958
return files, nil
@@ -1265,7 +1266,7 @@ func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, params *Event
12651266
body := replaceWithReplacer(c.Body, replacer)
12661267
subject := replaceWithReplacer(c.Subject, replacer)
12671268
startTime := time.Now()
1268-
var files []mail.File
1269+
var files []*mail.File
12691270
fileAttachments := make([]string, 0, len(c.Attachments))
12701271
for _, attachment := range c.Attachments {
12711272
if attachment == dataprovider.RetentionReportPlaceHolder {
@@ -1283,7 +1284,18 @@ func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, params *Event
12831284
if err != nil {
12841285
return err
12851286
}
1286-
res, err := getMailAttachments(user, fileAttachments, replacer)
1287+
user, err = getUserForEventAction(user)
1288+
if err != nil {
1289+
return err
1290+
}
1291+
connectionID := fmt.Sprintf("%s_%s", protocolEventAction, xid.New().String())
1292+
err = user.CheckFsRoot(connectionID)
1293+
defer user.CloseFs() //nolint:errcheck
1294+
if err != nil {
1295+
return fmt.Errorf("error getting email attachments, unable to check root fs for user %q: %w", user.Username, err)
1296+
}
1297+
conn := NewBaseConnection(connectionID, protocolEventAction, "", "", user)
1298+
res, err := getMailAttachments(conn, fileAttachments, replacer)
12871299
if err != nil {
12881300
return err
12891301
}

0 commit comments

Comments
 (0)