diff --git a/RELEASE.md b/RELEASE.md index b83765a..6e26214 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,8 @@ # Changelog +## [1.2.1] - 2025-xx-xx +- Add debug logging to SMTP. + ## [1.2.0] - 2025-09-04 - Added support for YmdHis Date and Base64 template functions - Improved campaign review details diff --git a/backend/log/gomail.go b/backend/log/gomail.go new file mode 100644 index 0000000..42fdbe6 --- /dev/null +++ b/backend/log/gomail.go @@ -0,0 +1,54 @@ +package log + +import ( + "fmt" + + "github.com/wneessen/go-mail/log" + "go.uber.org/zap" +) + +// GoMailLoggerAdapter adapts zap.SugaredLogger to go-mail's Logger interface +type GoMailLoggerAdapter struct { + logger *zap.SugaredLogger +} + +func (z *GoMailLoggerAdapter) formatLogMessage(l log.Log) string { + direction := "CLIENT" + if l.Direction == log.DirServerToClient { + direction = "SERVER" + } + + // format the message with arguments if present + msg := l.Format + if len(l.Messages) > 0 { + // create interface slice for fmt.Sprintf + args := make([]interface{}, len(l.Messages)) + for i, m := range l.Messages { + args[i] = m + } + msg = fmt.Sprintf(l.Format, args...) + } + + return fmt.Sprintf("SMTP %s: %s", direction, msg) +} + +func (z *GoMailLoggerAdapter) Debugf(l log.Log) { + z.logger.Debug(z.formatLogMessage(l)) +} + +func (z *GoMailLoggerAdapter) Infof(l log.Log) { + z.logger.Info(z.formatLogMessage(l)) +} + +func (z *GoMailLoggerAdapter) Warnf(l log.Log) { + z.logger.Warn(z.formatLogMessage(l)) +} + +func (z *GoMailLoggerAdapter) Errorf(l log.Log) { + z.logger.Error(z.formatLogMessage(l)) +} + +// NewGoMailLoggerAdapter creates a new go-mail logger adapter +func NewGoMailLoggerAdapter(logger *zap.SugaredLogger) *GoMailLoggerAdapter { + return &GoMailLoggerAdapter{logger: logger} +} diff --git a/backend/service/campaign.go b/backend/service/campaign.go index a048480..52da103 100644 --- a/backend/service/campaign.go +++ b/backend/service/campaign.go @@ -24,6 +24,7 @@ import ( "github.com/phishingclub/phishingclub/data" "github.com/phishingclub/phishingclub/database" "github.com/phishingclub/phishingclub/errs" + "github.com/phishingclub/phishingclub/log" "github.com/phishingclub/phishingclub/model" "github.com/phishingclub/phishingclub/repository" "github.com/phishingclub/phishingclub/validate" @@ -2020,6 +2021,8 @@ func (c *Campaign) sendCampaignMessages( // Try CRAM-MD5 first when credentials are provided emailOptionsCRAM5 := append(emailOptions, mail.WithSMTPAuth(mail.SMTPAuthCramMD5)) mc, _ = mail.NewClient(smtpHost.String(), emailOptionsCRAM5...) + mc.SetLogger(log.NewGoMailLoggerAdapter(c.Logger)) + mc.SetDebugLog(true) if build.Flags.Production { mc.SetTLSPolicy(mail.TLSMandatory) } else { @@ -2033,12 +2036,12 @@ func (c *Campaign) sendCampaignMessages( strings.Contains(err.Error(), "538 ") || strings.Contains(err.Error(), "CRAM-MD5") || strings.Contains(err.Error(), "authentication failed")) { - c.Logger.Warnw("CRAM-MD5 authentication failed, trying PLAIN auth", "error", err) + c.Logger.Debugf("CRAM-MD5 authentication failed, trying PLAIN auth", "error", err) emailOptionsBasic := emailOptions - if build.Flags.Production { - emailOptionsBasic = append(emailOptions, mail.WithSMTPAuth(mail.SMTPAuthPlain)) - } + emailOptionsBasic = append(emailOptions, mail.WithSMTPAuth(mail.SMTPAuthPlain)) mc, _ = mail.NewClient(smtpHost.String(), emailOptionsBasic...) + mc.SetLogger(log.NewGoMailLoggerAdapter(c.Logger)) + mc.SetDebugLog(true) if build.Flags.Production { mc.SetTLSPolicy(mail.TLSMandatory) } else { @@ -2049,6 +2052,8 @@ func (c *Campaign) sendCampaignMessages( } else { // No credentials provided, try without authentication (e.g., local postfix) mc, _ = mail.NewClient(smtpHost.String(), emailOptions...) + mc.SetLogger(log.NewGoMailLoggerAdapter(c.Logger)) + mc.SetDebugLog(true) if build.Flags.Production { mc.SetTLSPolicy(mail.TLSMandatory) } else { diff --git a/backend/service/smtpConfiguration.go b/backend/service/smtpConfiguration.go index 4921acb..4d0809c 100644 --- a/backend/service/smtpConfiguration.go +++ b/backend/service/smtpConfiguration.go @@ -12,6 +12,7 @@ import ( "github.com/phishingclub/phishingclub/build" "github.com/phishingclub/phishingclub/data" "github.com/phishingclub/phishingclub/errs" + "github.com/phishingclub/phishingclub/log" "github.com/phishingclub/phishingclub/model" "github.com/phishingclub/phishingclub/repository" "github.com/phishingclub/phishingclub/validate" @@ -277,13 +278,14 @@ func (s *SMTPConfiguration) SendTestEmail( } // send mail var mc *mail.Client - // Try different authentication methods based on configuration // If username is provided, use authentication; otherwise try without auth first if un := username.String(); len(un) > 0 { // Try CRAM-MD5 first when credentials are provided emailOptionsCRAM5 := append(emailOptions, mail.WithSMTPAuth(mail.SMTPAuthCramMD5)) mc, _ = mail.NewClient(smtpHost.String(), emailOptionsCRAM5...) + mc.SetLogger(log.NewGoMailLoggerAdapter(s.Logger)) + mc.SetDebugLog(true) if build.Flags.Production { mc.SetTLSPolicy(mail.TLSMandatory) } else { @@ -297,12 +299,12 @@ func (s *SMTPConfiguration) SendTestEmail( strings.Contains(err.Error(), "538 ") || strings.Contains(err.Error(), "CRAM-MD5") || strings.Contains(err.Error(), "authentication failed")) { - s.Logger.Warnw("CRAM-MD5 authentication failed, trying PLAIN auth", "error", err) + s.Logger.Debugf("CRAM-MD5 authentication failed, trying PLAIN auth", "error", err) emailOptionsBasic := emailOptions - if build.Flags.Production { - emailOptionsBasic = append(emailOptions, mail.WithSMTPAuth(mail.SMTPAuthPlain)) - } + emailOptionsBasic = append(emailOptions, mail.WithSMTPAuth(mail.SMTPAuthPlain)) mc, _ = mail.NewClient(smtpHost.String(), emailOptionsBasic...) + mc.SetLogger(log.NewGoMailLoggerAdapter(s.Logger)) + mc.SetDebugLog(true) if build.Flags.Production { mc.SetTLSPolicy(mail.TLSMandatory) } else { @@ -313,6 +315,8 @@ func (s *SMTPConfiguration) SendTestEmail( } else { // No credentials provided, try without authentication (e.g., local postfix) mc, _ = mail.NewClient(smtpHost.String(), emailOptions...) + mc.SetLogger(log.NewGoMailLoggerAdapter(s.Logger)) + mc.SetDebugLog(true) if build.Flags.Production { mc.SetTLSPolicy(mail.TLSMandatory) } else {