diff --git a/backend/database/smtpConfiguration.go b/backend/database/smtpConfiguration.go index 77649039..ff8609d8 100644 --- a/backend/database/smtpConfiguration.go +++ b/backend/database/smtpConfiguration.go @@ -23,6 +23,9 @@ type SMTPConfiguration struct { Username string `gorm:"not null;"` Password string `gorm:"not null;"` IgnoreCertErrors bool `gorm:"not null;"` + // Helo is the hostname sent in the SMTP HELO/EHLO greeting. + // empty means go-mail falls back to the machine hostname. + Helo string `gorm:"not null;default:'';"` // back-reference Headers []*SMTPHeader diff --git a/backend/model/smtpConfiguration.go b/backend/model/smtpConfiguration.go index 61a18984..794de5d6 100644 --- a/backend/model/smtpConfiguration.go +++ b/backend/model/smtpConfiguration.go @@ -20,6 +20,7 @@ type SMTPConfiguration struct { Username nullable.Nullable[vo.OptionalString255] `json:"username"` Password nullable.Nullable[vo.OptionalString255] `json:"password"` IgnoreCertErrors nullable.Nullable[bool] `json:"ignoreCertErrors"` + Helo nullable.Nullable[vo.OptionalString255] `json:"helo"` CompanyID nullable.Nullable[uuid.UUID] `json:"companyID"` Company *Company `json:"company"` Headers []*SMTPHeader `json:"headers"` @@ -89,6 +90,12 @@ func (s *SMTPConfiguration) ToDBMap() map[string]any { m["ignore_cert_errors"] = ignoreCertErrors } } + if s.Helo.IsSpecified() { + m["helo"] = "" + if helo, err := s.Helo.Get(); err == nil { + m["helo"] = helo.String() + } + } if v, err := s.CompanyID.Get(); err == nil { m["company_id"] = v.String() } diff --git a/backend/repository/smtpConfiguration.go b/backend/repository/smtpConfiguration.go index 562e658e..880dbe74 100644 --- a/backend/repository/smtpConfiguration.go +++ b/backend/repository/smtpConfiguration.go @@ -283,6 +283,7 @@ func ToSMTPConfiguration( username := nullable.NewNullableWithValue(*vo.NewOptionalString255Must(row.Username)) password := nullable.NewNullableWithValue(*vo.NewOptionalString255Must(row.Password)) ignoreCertErrors := nullable.NewNullableWithValue(row.IgnoreCertErrors) + helo := nullable.NewNullableWithValue(*vo.NewOptionalString255Must(row.Helo)) return &model.SMTPConfiguration{ ID: id, @@ -295,6 +296,7 @@ func ToSMTPConfiguration( Username: username, Password: password, IgnoreCertErrors: ignoreCertErrors, + Helo: helo, Headers: headers, } } diff --git a/backend/service/campaign.go b/backend/service/campaign.go index 5ef29809..23faab17 100644 --- a/backend/service/campaign.go +++ b/backend/service/campaign.go @@ -3112,6 +3112,13 @@ func (c *Campaign) sendCampaignMessages( }, ), } + // use a custom HELO/EHLO hostname when set, otherwise go-mail + // falls back to the machine hostname + if helo, err := smtpConfig.Helo.Get(); err == nil { + if h := helo.String(); len(h) > 0 { + emailOptions = append(emailOptions, mail.WithHELO(h)) + } + } // setup authentication if provided username, err := smtpConfig.Username.Get() if err != nil { @@ -5554,6 +5561,14 @@ func (c *Campaign) sendSingleEmailSMTP( ), } + // use a custom HELO/EHLO hostname when set, otherwise go-mail + // falls back to the machine hostname + if helo, err := smtpConfig.Helo.Get(); err == nil { + if h := helo.String(); len(h) > 0 { + emailOptions = append(emailOptions, mail.WithHELO(h)) + } + } + // setup authentication if provided username, err := smtpConfig.Username.Get() if err != nil { diff --git a/backend/service/email.go b/backend/service/email.go index 2217e579..53f2dd03 100644 --- a/backend/service/email.go +++ b/backend/service/email.go @@ -532,6 +532,13 @@ func (m *Email) SendTestEmail( }, ), } + // use a custom HELO/EHLO hostname when set, otherwise go-mail + // falls back to the machine hostname + if helo, err := smtp.Helo.Get(); err == nil { + if h := helo.String(); len(h) > 0 { + emailOptions = append(emailOptions, mail.WithHELO(h)) + } + } // setup authentication if provided username, err := smtp.Username.Get() if err != nil { diff --git a/backend/service/smtpConfiguration.go b/backend/service/smtpConfiguration.go index b3fdba2b..c352c645 100644 --- a/backend/service/smtpConfiguration.go +++ b/backend/service/smtpConfiguration.go @@ -242,6 +242,13 @@ func (s *SMTPConfiguration) SendTestEmail( }, ), } + // use a custom HELO/EHLO hostname when set, otherwise go-mail + // falls back to the machine hostname + if helo, err := smtpConfig.Helo.Get(); err == nil { + if h := helo.String(); len(h) > 0 { + emailOptions = append(emailOptions, mail.WithHELO(h)) + } + } // setup authentication if provided username, err := smtpConfig.Username.Get() if err != nil { @@ -454,6 +461,9 @@ func (s *SMTPConfiguration) UpdateByID( if v, err := incoming.IgnoreCertErrors.Get(); err == nil { current.IgnoreCertErrors.Set(v) } + if v, err := incoming.Helo.Get(); err == nil { + current.Helo.Set(v) + } if err := incoming.Validate(); err != nil { s.Logger.Errorw("failed to update SMTP configuration", "error", err) return err @@ -656,6 +666,13 @@ func (s *SMTPConfiguration) SendMessages( }, ), } + // use a custom HELO/EHLO hostname when set, otherwise go-mail + // falls back to the machine hostname + if helo, err := smtpConfig.Helo.Get(); err == nil { + if h := helo.String(); len(h) > 0 { + emailOptions = append(emailOptions, mail.WithHELO(h)) + } + } username, err := smtpConfig.Username.Get() if err != nil { s.Logger.Errorw("failed to get smtp username", "error", err) diff --git a/frontend/src/lib/api/api.js b/frontend/src/lib/api/api.js index a4458b01..93a58067 100644 --- a/frontend/src/lib/api/api.js +++ b/frontend/src/lib/api/api.js @@ -2016,6 +2016,7 @@ export class API { * @param {string} configuration.username * @param {string} configuration.password * @param {boolean} configuration.ignoreCertErrors + * @param {string} configuration.helo * @param {string} configuration.companyID * @returns */ @@ -2034,6 +2035,7 @@ export class API { * @param {string} configuration.username * @param {string} configuration.password * @param {boolean} configuration.ignoreCertErrors + * @param {string} configuration.helo * @param {string} configuration.companyID * @returns {Promise} */ diff --git a/frontend/src/routes/smtp-configuration/+page.svelte b/frontend/src/routes/smtp-configuration/+page.svelte index e968fa47..e865cce4 100644 --- a/frontend/src/routes/smtp-configuration/+page.svelte +++ b/frontend/src/routes/smtp-configuration/+page.svelte @@ -54,7 +54,8 @@ port: null, username: null, password: null, - ignoreCertErrors: null + ignoreCertErrors: null, + helo: null }; let headerFormValues = { id: null, @@ -206,6 +207,7 @@ username: formValues.username, password: formValues.password, ignoreCertErrors: formValues.ignoreCertErrors, + helo: formValues.helo, companyID: contextCompanyID }); if (!res.success) { @@ -233,6 +235,7 @@ username: formValues.username, password: formValues.password, ignoreCertErrors: formValues.ignoreCertErrors, + helo: formValues.helo, companyID: formValues.companyID }); if (res.success) { @@ -381,7 +384,8 @@ port: configuration.port, username: configuration.username, password: configuration.password, - ignoreCertErrors: configuration.ignoreCertErrors + ignoreCertErrors: configuration.ignoreCertErrors, + helo: configuration.helo }; }; @@ -560,6 +564,15 @@ bind:value={formValues.port} placeholder="587">Port + HELO/EHLO Hostname