lint fixes
This commit is contained in:
110
.golangci.yml
110
.golangci.yml
@@ -4,77 +4,123 @@ run:
|
|||||||
issues-exit-code: 1
|
issues-exit-code: 1
|
||||||
tests: true
|
tests: true
|
||||||
build-tags: []
|
build-tags: []
|
||||||
skip-dirs: []
|
skip-dirs:
|
||||||
|
- mocks
|
||||||
skip-dirs-use-default: true
|
skip-dirs-use-default: true
|
||||||
skip-files: []
|
skip-files: []
|
||||||
modules-download-mode: readonly
|
modules-download-mode: readonly
|
||||||
allow-parallel-runners: false
|
|
||||||
|
|
||||||
output:
|
output:
|
||||||
format: colored-line-number
|
format: colored-line-number
|
||||||
print-issued-lines: true
|
print-issued-lines: true
|
||||||
print-linter-name: true
|
print-linter-name: true
|
||||||
uniq-by-line: true
|
|
||||||
path-prefix: ""
|
|
||||||
sort-results: true
|
sort-results: true
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
|
decorder:
|
||||||
|
dec-order:
|
||||||
|
- const
|
||||||
|
- var
|
||||||
|
- type
|
||||||
|
- func
|
||||||
|
dogsled:
|
||||||
|
max-blank-identifiers: 3
|
||||||
errcheck:
|
errcheck:
|
||||||
|
check-type-assertions: true
|
||||||
check-blank: true
|
check-blank: true
|
||||||
|
errchkjson:
|
||||||
|
report-no-exported: true
|
||||||
|
exhaustive:
|
||||||
|
check:
|
||||||
|
- switch
|
||||||
|
- map
|
||||||
|
default-signifies-exhaustive: true
|
||||||
gocognit:
|
gocognit:
|
||||||
min-complexity: 15
|
min-complexity: 15
|
||||||
nestif:
|
nestif:
|
||||||
min-complexity: 4
|
min-complexity: 5
|
||||||
gocritic:
|
gocritic:
|
||||||
enabled-tags:
|
enabled-tags:
|
||||||
|
- diagnostic
|
||||||
|
- style
|
||||||
- performance
|
- performance
|
||||||
|
gofmt:
|
||||||
|
simplify: true
|
||||||
|
rewrite-rules:
|
||||||
|
- pattern: 'interface{}'
|
||||||
|
replacement: 'any'
|
||||||
|
- pattern: 'a[b:len(a)]'
|
||||||
|
replacement: 'a[b:]'
|
||||||
gofumpt:
|
gofumpt:
|
||||||
lang-version: "1.18"
|
extra-rules: true
|
||||||
gosimple:
|
|
||||||
go: "1.18"
|
|
||||||
checks: [ "all" ]
|
|
||||||
govet:
|
govet:
|
||||||
check-shadowing: true
|
check-shadowing: true
|
||||||
enable:
|
grouper:
|
||||||
- atomicalign
|
const-require-single-const: true
|
||||||
- shadow
|
import-require-single-import: true
|
||||||
|
var-require-single-var: true
|
||||||
misspell:
|
misspell:
|
||||||
locale: US
|
locale: US
|
||||||
staticcheck:
|
usestdlibvars:
|
||||||
go: "1.18"
|
time-month: true
|
||||||
checks: [ "all" ]
|
time-layout: true
|
||||||
stylecheck:
|
crypto-hash: true
|
||||||
go: "1.18"
|
default-rpc-path: true
|
||||||
|
os-dev-null: true
|
||||||
|
sql-isolation-level: true
|
||||||
|
tls-signature-scheme: true
|
||||||
|
constant-kind: true
|
||||||
unparam:
|
unparam:
|
||||||
check-exported: true
|
check-exported: true
|
||||||
unused:
|
|
||||||
go: "1.18"
|
|
||||||
gci:
|
|
||||||
sections:
|
|
||||||
- standard
|
|
||||||
- default
|
|
||||||
- prefix(gitlab.com/etke.cc/postmoogle)
|
|
||||||
section-separators:
|
|
||||||
- newLine
|
|
||||||
linters:
|
linters:
|
||||||
disable-all: false
|
disable-all: false
|
||||||
enable:
|
enable:
|
||||||
- megacheck
|
- asasalint
|
||||||
- govet
|
- asciicheck
|
||||||
|
- bidichk
|
||||||
|
- bodyclose
|
||||||
|
- containedctx
|
||||||
|
- decorder
|
||||||
|
- dogsled
|
||||||
|
- dupl
|
||||||
|
- dupword
|
||||||
|
- durationcheck
|
||||||
- errcheck
|
- errcheck
|
||||||
- gci
|
- errchkjson
|
||||||
|
- errname
|
||||||
|
- errorlint
|
||||||
|
- execinquery
|
||||||
|
- exhaustive
|
||||||
|
- exportloopref
|
||||||
|
- forcetypeassert
|
||||||
|
- gochecknoinits
|
||||||
- gocognit
|
- gocognit
|
||||||
- nestif
|
- gocritic
|
||||||
# - gocritic # ref: https://github.com/golangci/golangci-lint/issues/2649#issue-1170906525
|
- gocyclo
|
||||||
|
- goerr113
|
||||||
|
- gofmt
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- goimports
|
- goimports
|
||||||
|
- gosec
|
||||||
- gosimple
|
- gosimple
|
||||||
|
- gosmopolitan
|
||||||
- govet
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- makezero
|
||||||
|
- mirror
|
||||||
- misspell
|
- misspell
|
||||||
|
- nestif
|
||||||
|
- nolintlint
|
||||||
|
- prealloc
|
||||||
|
- predeclared
|
||||||
|
- revive
|
||||||
|
- sqlclosecheck
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- stylecheck
|
- unconvert
|
||||||
- unparam
|
- unparam
|
||||||
- unused
|
- unused
|
||||||
|
- usestdlibvars
|
||||||
|
- wastedassign
|
||||||
fast: false
|
fast: false
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func (b *Bot) allowUsers(actorID id.UserID) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) allowAnyone(actorID id.UserID, targetRoomID id.RoomID) bool {
|
func (b *Bot) allowAnyone(_ id.UserID, _ id.RoomID) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ func (b *Bot) allowOwner(actorID id.UserID, targetRoomID id.RoomID) bool {
|
|||||||
return owner == actorID.String()
|
return owner == actorID.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) allowAdmin(actorID id.UserID, targetRoomID id.RoomID) bool {
|
func (b *Bot) allowAdmin(actorID id.UserID, _ id.RoomID) bool {
|
||||||
return mxidwc.Match(actorID.String(), b.allowedAdmins)
|
return mxidwc.Match(actorID.String(), b.allowedAdmins)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,14 +91,14 @@ func New(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Error message to the log and matrix room
|
// Error message to the log and matrix room
|
||||||
func (b *Bot) Error(ctx context.Context, message string, args ...interface{}) {
|
func (b *Bot) Error(ctx context.Context, message string, args ...any) {
|
||||||
evt := eventFromContext(ctx)
|
evt := eventFromContext(ctx)
|
||||||
threadID := threadIDFromContext(ctx)
|
threadID := threadIDFromContext(ctx)
|
||||||
if threadID == "" {
|
if threadID == "" {
|
||||||
threadID = linkpearl.EventParent(evt.ID, evt.Content.AsMessage())
|
threadID = linkpearl.EventParent(evt.ID, evt.Content.AsMessage())
|
||||||
}
|
}
|
||||||
|
|
||||||
err := fmt.Errorf(message, args...)
|
err := fmt.Errorf(message, args...) //nolint:goerr113 // we have to
|
||||||
b.log.Error().Err(err).Msg(err.Error())
|
b.log.Error().Err(err).Msg(err.Error())
|
||||||
if evt == nil {
|
if evt == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package bot
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -372,7 +373,7 @@ func (b *Bot) handle(ctx context.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
b.log.Error().Err(err).Msg("cannot send typing notification")
|
b.log.Error().Err(err).Msg("cannot send typing notification")
|
||||||
}
|
}
|
||||||
defer b.lp.GetClient().UserTyping(evt.RoomID, false, 30*time.Second) //nolint:errcheck
|
defer b.lp.GetClient().UserTyping(evt.RoomID, false, 30*time.Second) //nolint:errcheck // we don't care
|
||||||
|
|
||||||
if !cmd.allowed(evt.Sender, evt.RoomID) {
|
if !cmd.allowed(evt.Sender, evt.RoomID) {
|
||||||
b.lp.SendNotice(evt.RoomID, "not allowed to do that, kupo")
|
b.lp.SendNotice(evt.RoomID, "not allowed to do that, kupo")
|
||||||
@@ -413,9 +414,9 @@ func (b *Bot) handle(ctx context.Context) {
|
|||||||
case commandBanlistTotals:
|
case commandBanlistTotals:
|
||||||
b.runBanlistTotals(ctx)
|
b.runBanlistTotals(ctx)
|
||||||
case commandBanlistAdd:
|
case commandBanlistAdd:
|
||||||
b.runBanlistAdd(ctx, commandSlice)
|
b.runBanlistChange(ctx, "add", commandSlice)
|
||||||
case commandBanlistRemove:
|
case commandBanlistRemove:
|
||||||
b.runBanlistRemove(ctx, commandSlice)
|
b.runBanlistChange(ctx, "remove", commandSlice)
|
||||||
case commandBanlistReset:
|
case commandBanlistReset:
|
||||||
b.runBanlistReset(ctx)
|
b.runBanlistReset(ctx)
|
||||||
case commandMailboxes:
|
case commandMailboxes:
|
||||||
@@ -543,7 +544,7 @@ func (b *Bot) runSend(ctx context.Context) {
|
|||||||
b.runSendCommand(ctx, cfg, tos, subject, body, htmlBody)
|
b.runSendCommand(ctx, cfg, tos, subject, body, htmlBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) getSendDetails(ctx context.Context) (string, string, string, bool) {
|
func (b *Bot) getSendDetails(ctx context.Context) (to, subject, body string, ok bool) {
|
||||||
evt := eventFromContext(ctx)
|
evt := eventFromContext(ctx)
|
||||||
if !b.allowSend(evt.Sender, evt.RoomID) {
|
if !b.allowSend(evt.Sender, evt.RoomID) {
|
||||||
return "", "", "", false
|
return "", "", "", false
|
||||||
@@ -556,8 +557,8 @@ func (b *Bot) getSendDetails(ctx context.Context) (string, string, string, bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
commandSlice := b.parseCommand(evt.Content.AsMessage().Body, false)
|
commandSlice := b.parseCommand(evt.Content.AsMessage().Body, false)
|
||||||
to, subject, body, err := utils.ParseSend(commandSlice)
|
to, subject, body, err = utils.ParseSend(commandSlice)
|
||||||
if err == utils.ErrInvalidArgs {
|
if errors.Is(err, utils.ErrInvalidArgs) {
|
||||||
b.lp.SendNotice(evt.RoomID, fmt.Sprintf(
|
b.lp.SendNotice(evt.RoomID, fmt.Sprintf(
|
||||||
"Usage:\n"+
|
"Usage:\n"+
|
||||||
"```\n"+
|
"```\n"+
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (b *Bot) sendMailboxes(ctx context.Context) {
|
|||||||
evt := eventFromContext(ctx)
|
evt := eventFromContext(ctx)
|
||||||
mailboxes := map[string]config.Room{}
|
mailboxes := map[string]config.Room{}
|
||||||
slice := []string{}
|
slice := []string{}
|
||||||
b.rooms.Range(func(key any, value any) bool {
|
b.rooms.Range(func(key, value any) bool {
|
||||||
if key == nil {
|
if key == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -37,12 +37,12 @@ func (b *Bot) sendMailboxes(ctx context.Context) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
config, err := b.cfg.GetRoom(roomID)
|
cfg, err := b.cfg.GetRoom(roomID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.log.Error().Err(err).Msg("cannot retrieve settings")
|
b.log.Error().Err(err).Msg("cannot retrieve settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
mailboxes[mailbox] = config
|
mailboxes[mailbox] = cfg
|
||||||
slice = append(slice, mailbox)
|
slice = append(slice, mailbox)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@@ -80,7 +80,10 @@ func (b *Bot) runDelete(ctx context.Context, commandSlice []string) {
|
|||||||
b.lp.SendNotice(evt.RoomID, "mailbox does not exists, kupo", linkpearl.RelatesTo(evt.ID))
|
b.lp.SendNotice(evt.RoomID, "mailbox does not exists, kupo", linkpearl.RelatesTo(evt.ID))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
roomID := v.(id.RoomID)
|
roomID, ok := v.(id.RoomID)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
b.rooms.Delete(mailbox)
|
b.rooms.Delete(mailbox)
|
||||||
err := b.cfg.SetRoom(roomID, config.Room{})
|
err := b.cfg.SetRoom(roomID, config.Room{})
|
||||||
@@ -350,7 +353,7 @@ func (b *Bot) runBanlistTotals(ctx context.Context) {
|
|||||||
b.addBanlistTimeline(ctx, true)
|
b.addBanlistTimeline(ctx, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) runBanlistAuth(ctx context.Context, commandSlice []string) {
|
func (b *Bot) runBanlistAuth(ctx context.Context, commandSlice []string) { //nolint:dupl // not in that case
|
||||||
evt := eventFromContext(ctx)
|
evt := eventFromContext(ctx)
|
||||||
cfg := b.cfg.GetBot()
|
cfg := b.cfg.GetBot()
|
||||||
if len(commandSlice) < 2 {
|
if len(commandSlice) < 2 {
|
||||||
@@ -377,7 +380,7 @@ func (b *Bot) runBanlistAuth(ctx context.Context, commandSlice []string) {
|
|||||||
b.lp.SendNotice(evt.RoomID, "auth banning has been updated", linkpearl.RelatesTo(evt.ID))
|
b.lp.SendNotice(evt.RoomID, "auth banning has been updated", linkpearl.RelatesTo(evt.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) runBanlistAuto(ctx context.Context, commandSlice []string) {
|
func (b *Bot) runBanlistAuto(ctx context.Context, commandSlice []string) { //nolint:dupl // not in that case
|
||||||
evt := eventFromContext(ctx)
|
evt := eventFromContext(ctx)
|
||||||
cfg := b.cfg.GetBot()
|
cfg := b.cfg.GetBot()
|
||||||
if len(commandSlice) < 2 {
|
if len(commandSlice) < 2 {
|
||||||
@@ -404,7 +407,7 @@ func (b *Bot) runBanlistAuto(ctx context.Context, commandSlice []string) {
|
|||||||
b.lp.SendNotice(evt.RoomID, "auto banning has been updated", linkpearl.RelatesTo(evt.ID))
|
b.lp.SendNotice(evt.RoomID, "auto banning has been updated", linkpearl.RelatesTo(evt.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) runBanlistAdd(ctx context.Context, commandSlice []string) {
|
func (b *Bot) runBanlistChange(ctx context.Context, mode string, commandSlice []string) {
|
||||||
evt := eventFromContext(ctx)
|
evt := eventFromContext(ctx)
|
||||||
if len(commandSlice) < 2 {
|
if len(commandSlice) < 2 {
|
||||||
b.runBanlist(ctx, commandSlice)
|
b.runBanlist(ctx, commandSlice)
|
||||||
@@ -416,37 +419,13 @@ func (b *Bot) runBanlistAdd(ctx context.Context, commandSlice []string) {
|
|||||||
}
|
}
|
||||||
banlist := b.cfg.GetBanlist()
|
banlist := b.cfg.GetBanlist()
|
||||||
|
|
||||||
ips := commandSlice[1:]
|
var action func(net.Addr)
|
||||||
for _, ip := range ips {
|
if mode == "remove" {
|
||||||
addr, err := net.ResolveIPAddr("ip", ip)
|
action = banlist.Remove
|
||||||
if err != nil {
|
} else {
|
||||||
b.Error(ctx, "cannot add %s to banlist: %v", ip, err)
|
action = banlist.Add
|
||||||
return
|
|
||||||
}
|
|
||||||
banlist.Add(addr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := b.cfg.SetBanlist(banlist)
|
|
||||||
if err != nil {
|
|
||||||
b.Error(ctx, "cannot set banlist: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
b.lp.SendNotice(evt.RoomID, "banlist has been updated, kupo", linkpearl.RelatesTo(evt.ID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bot) runBanlistRemove(ctx context.Context, commandSlice []string) {
|
|
||||||
evt := eventFromContext(ctx)
|
|
||||||
if len(commandSlice) < 2 {
|
|
||||||
b.runBanlist(ctx, commandSlice)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !b.cfg.GetBot().BanlistEnabled() {
|
|
||||||
b.lp.SendNotice(evt.RoomID, "banlist is disabled, you have to enable it first, kupo", linkpearl.RelatesTo(evt.ID))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
banlist := b.cfg.GetBanlist()
|
|
||||||
|
|
||||||
ips := commandSlice[1:]
|
ips := commandSlice[1:]
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
addr, err := net.ResolveIPAddr("ip", ip)
|
addr, err := net.ResolveIPAddr("ip", ip)
|
||||||
@@ -454,7 +433,7 @@ func (b *Bot) runBanlistRemove(ctx context.Context, commandSlice []string) {
|
|||||||
b.Error(ctx, "cannot remove %s from banlist: %v", ip, err)
|
b.Error(ctx, "cannot remove %s from banlist: %v", ip, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
banlist.Remove(addr)
|
action(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := b.cfg.SetBanlist(banlist)
|
err := b.cfg.SetBanlist(banlist)
|
||||||
|
|||||||
45
bot/email.go
45
bot/email.go
@@ -15,14 +15,12 @@ import (
|
|||||||
"gitlab.com/etke.cc/postmoogle/utils"
|
"gitlab.com/etke.cc/postmoogle/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// account data keys
|
|
||||||
const (
|
const (
|
||||||
|
// account data keys
|
||||||
acMessagePrefix = "cc.etke.postmoogle.message"
|
acMessagePrefix = "cc.etke.postmoogle.message"
|
||||||
acLastEventPrefix = "cc.etke.postmoogle.last"
|
acLastEventPrefix = "cc.etke.postmoogle.last"
|
||||||
)
|
|
||||||
|
|
||||||
// event keys
|
// event keys
|
||||||
const (
|
|
||||||
eventMessageIDkey = "cc.etke.postmoogle.messageID"
|
eventMessageIDkey = "cc.etke.postmoogle.messageID"
|
||||||
eventReferencesKey = "cc.etke.postmoogle.references"
|
eventReferencesKey = "cc.etke.postmoogle.references"
|
||||||
eventInReplyToKey = "cc.etke.postmoogle.inReplyTo"
|
eventInReplyToKey = "cc.etke.postmoogle.inReplyTo"
|
||||||
@@ -33,6 +31,8 @@ const (
|
|||||||
eventCcKey = "cc.etke.postmoogle.cc"
|
eventCcKey = "cc.etke.postmoogle.cc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrNoRoom = errors.New("room not found")
|
||||||
|
|
||||||
// SetSendmail sets mail sending func to the bot
|
// SetSendmail sets mail sending func to the bot
|
||||||
func (b *Bot) SetSendmail(sendmail func(string, string, string) error) {
|
func (b *Bot) SetSendmail(sendmail func(string, string, string) error) {
|
||||||
b.sendmail = sendmail
|
b.sendmail = sendmail
|
||||||
@@ -40,8 +40,8 @@ func (b *Bot) SetSendmail(sendmail func(string, string, string) error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) shouldQueue(msg string) bool {
|
func (b *Bot) shouldQueue(msg string) bool {
|
||||||
errors := strings.Split(msg, ";")
|
errs := strings.Split(msg, ";")
|
||||||
for _, err := range errors {
|
for _, err := range errs {
|
||||||
errParts := strings.Split(strings.TrimSpace(err), ":")
|
errParts := strings.Split(strings.TrimSpace(err), ":")
|
||||||
if len(errParts) < 2 {
|
if len(errParts) < 2 {
|
||||||
continue
|
continue
|
||||||
@@ -114,10 +114,10 @@ func (b *Bot) GetIFOptions(roomID id.RoomID) email.IncomingFilteringOptions {
|
|||||||
// IncomingEmail sends incoming email to matrix room
|
// IncomingEmail sends incoming email to matrix room
|
||||||
//
|
//
|
||||||
//nolint:gocognit // TODO
|
//nolint:gocognit // TODO
|
||||||
func (b *Bot) IncomingEmail(ctx context.Context, email *email.Email) error {
|
func (b *Bot) IncomingEmail(ctx context.Context, eml *email.Email) error {
|
||||||
roomID, ok := b.GetMapping(email.Mailbox(true))
|
roomID, ok := b.GetMapping(eml.Mailbox(true))
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("room not found")
|
return ErrNoRoom
|
||||||
}
|
}
|
||||||
cfg, err := b.cfg.GetRoom(roomID)
|
cfg, err := b.cfg.GetRoom(roomID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -129,18 +129,18 @@ func (b *Bot) IncomingEmail(ctx context.Context, email *email.Email) error {
|
|||||||
|
|
||||||
var threadID id.EventID
|
var threadID id.EventID
|
||||||
newThread := true
|
newThread := true
|
||||||
if email.InReplyTo != "" || email.References != "" {
|
if eml.InReplyTo != "" || eml.References != "" {
|
||||||
threadID = b.getThreadID(roomID, email.InReplyTo, email.References)
|
threadID = b.getThreadID(roomID, eml.InReplyTo, eml.References)
|
||||||
if threadID != "" {
|
if threadID != "" {
|
||||||
newThread = false
|
newThread = false
|
||||||
ctx = threadIDToContext(ctx, threadID)
|
ctx = threadIDToContext(ctx, threadID)
|
||||||
b.setThreadID(roomID, email.MessageID, threadID)
|
b.setThreadID(roomID, eml.MessageID, threadID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content := email.Content(threadID, cfg.ContentOptions())
|
content := eml.Content(threadID, cfg.ContentOptions())
|
||||||
eventID, serr := b.lp.Send(roomID, content)
|
eventID, serr := b.lp.Send(roomID, content)
|
||||||
if serr != nil {
|
if serr != nil {
|
||||||
if !strings.Contains(serr.Error(), "M_UNKNOWN") { // if it's not an unknown event event error
|
if !strings.Contains(serr.Error(), "M_UNKNOWN") { // if it's not an unknown event error
|
||||||
return serr
|
return serr
|
||||||
}
|
}
|
||||||
threadID = "" // unknown event edge case - remove existing thread ID to avoid complications
|
threadID = "" // unknown event edge case - remove existing thread ID to avoid complications
|
||||||
@@ -151,15 +151,15 @@ func (b *Bot) IncomingEmail(ctx context.Context, email *email.Email) error {
|
|||||||
ctx = threadIDToContext(ctx, threadID)
|
ctx = threadIDToContext(ctx, threadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.setThreadID(roomID, email.MessageID, threadID)
|
b.setThreadID(roomID, eml.MessageID, threadID)
|
||||||
b.setLastEventID(roomID, threadID, eventID)
|
b.setLastEventID(roomID, threadID, eventID)
|
||||||
|
|
||||||
if !cfg.NoInlines() {
|
if !cfg.NoInlines() {
|
||||||
b.sendFiles(ctx, roomID, email.InlineFiles, cfg.NoThreads(), threadID)
|
b.sendFiles(ctx, roomID, eml.InlineFiles, cfg.NoThreads(), threadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cfg.NoFiles() {
|
if !cfg.NoFiles() {
|
||||||
b.sendFiles(ctx, roomID, email.Files, cfg.NoThreads(), threadID)
|
b.sendFiles(ctx, roomID, eml.Files, cfg.NoThreads(), threadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if newThread && cfg.Autoreply() != "" {
|
if newThread && cfg.Autoreply() != "" {
|
||||||
@@ -255,8 +255,9 @@ func (b *Bot) sendAutoreply(roomID id.RoomID, threadID id.EventID) {
|
|||||||
b.saveSentMetadata(ctx, queued, meta.ThreadID, recipients, eml, cfg, "Autoreply has been sent")
|
b.saveSentMetadata(ctx, queued, meta.ThreadID, recipients, eml, cfg, "Autoreply has been sent")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) canReply(sender id.UserID, roomID id.RoomID) bool {
|
func (b *Bot) canReply(ctx context.Context) bool {
|
||||||
return b.allowSend(sender, roomID) && b.allowReply(sender, roomID)
|
evt := eventFromContext(ctx)
|
||||||
|
return b.allowSend(evt.Sender, evt.RoomID) && b.allowReply(evt.Sender, evt.RoomID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendEmailReply sends replies from matrix thread to email thread
|
// SendEmailReply sends replies from matrix thread to email thread
|
||||||
@@ -264,7 +265,7 @@ func (b *Bot) canReply(sender id.UserID, roomID id.RoomID) bool {
|
|||||||
//nolint:gocognit // TODO
|
//nolint:gocognit // TODO
|
||||||
func (b *Bot) SendEmailReply(ctx context.Context) {
|
func (b *Bot) SendEmailReply(ctx context.Context) {
|
||||||
evt := eventFromContext(ctx)
|
evt := eventFromContext(ctx)
|
||||||
if !b.canReply(evt.Sender, evt.RoomID) {
|
if !b.canReply(ctx) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfg, err := b.cfg.GetRoom(evt.RoomID)
|
cfg, err := b.cfg.GetRoom(evt.RoomID)
|
||||||
@@ -543,7 +544,7 @@ func (b *Bot) sendFiles(ctx context.Context, roomID id.RoomID, files []*utils.Fi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) getThreadID(roomID id.RoomID, messageID string, references string) id.EventID {
|
func (b *Bot) getThreadID(roomID id.RoomID, messageID, references string) id.EventID {
|
||||||
refs := []string{messageID}
|
refs := []string{messageID}
|
||||||
if references != "" {
|
if references != "" {
|
||||||
refs = append(refs, strings.Split(references, " ")...)
|
refs = append(refs, strings.Split(references, " ")...)
|
||||||
@@ -592,7 +593,7 @@ func (b *Bot) getLastEventID(roomID id.RoomID, threadID id.EventID) id.EventID {
|
|||||||
return threadID
|
return threadID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) setLastEventID(roomID id.RoomID, threadID id.EventID, eventID id.EventID) {
|
func (b *Bot) setLastEventID(roomID id.RoomID, threadID, eventID id.EventID) {
|
||||||
key := acLastEventPrefix + "." + threadID.String()
|
key := acLastEventPrefix + "." + threadID.String()
|
||||||
err := b.lp.SetRoomAccountData(roomID, key, map[string]string{"eventID": eventID.String()})
|
err := b.lp.SetRoomAccountData(roomID, key, map[string]string{"eventID": eventID.String()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ func (b *Bot) handleReaction(ctx context.Context) {
|
|||||||
ctx = threadIDToContext(ctx, threadID)
|
ctx = threadIDToContext(ctx, threadID)
|
||||||
linkpearl.ParseContent(evt, b.log)
|
linkpearl.ParseContent(evt, b.log)
|
||||||
|
|
||||||
switch action {
|
if action == commandSpamlistAdd {
|
||||||
case commandSpamlistAdd:
|
|
||||||
sender := linkpearl.EventField[string](&srcEvt.Content, eventFromKey)
|
sender := linkpearl.EventField[string](&srcEvt.Content, eventFromKey)
|
||||||
if sender == "" {
|
if sender == "" {
|
||||||
b.Error(ctx, "cannot get sender of the email")
|
b.Error(ctx, "cannot get sender of the email")
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ func initMatrix(cfg *config.Config) {
|
|||||||
Logger: log,
|
Logger: log,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// nolint // Fatal = panic, not os.Exit()
|
|
||||||
log.Fatal().Err(err).Msg("cannot initialize matrix bot")
|
log.Fatal().Err(err).Msg("cannot initialize matrix bot")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ func (e *Email) Content(threadID id.EventID, options *ContentOptions) *event.Con
|
|||||||
}
|
}
|
||||||
|
|
||||||
content := event.Content{
|
content := event.Content{
|
||||||
Raw: map[string]interface{}{
|
Raw: map[string]any{
|
||||||
options.MessageIDKey: e.MessageID,
|
options.MessageIDKey: e.MessageID,
|
||||||
options.InReplyToKey: e.InReplyTo,
|
options.InReplyToKey: e.InReplyTo,
|
||||||
options.ReferencesKey: e.References,
|
options.ReferencesKey: e.References,
|
||||||
@@ -231,7 +231,10 @@ func (e *Email) sign(domain, privkey string, data strings.Builder) string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return data.String()
|
return data.String()
|
||||||
}
|
}
|
||||||
signer := parsedkey.(crypto.Signer)
|
signer, ok := parsedkey.(crypto.Signer)
|
||||||
|
if !ok {
|
||||||
|
return data.String()
|
||||||
|
}
|
||||||
|
|
||||||
options := &dkim.SignOptions{
|
options := &dkim.SignOptions{
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
var styleRegex = regexp.MustCompile("<style((.|\n|\r)*?)<\\/style>")
|
var styleRegex = regexp.MustCompile("<style((.|\n|\r)*?)</style>")
|
||||||
|
|
||||||
// AddressValid checks if email address is valid
|
// AddressValid checks if email address is valid
|
||||||
func AddressValid(email string) bool {
|
func AddressValid(email string) bool {
|
||||||
|
|||||||
14
justfile
14
justfile
@@ -4,12 +4,6 @@ project := file_name(repo)
|
|||||||
gitlab_image := "registry." + repo + ":" + tag
|
gitlab_image := "registry." + repo + ":" + tag
|
||||||
etke_image := replace(gitlab_image, "gitlab.com", "etke.cc")
|
etke_image := replace(gitlab_image, "gitlab.com", "etke.cc")
|
||||||
|
|
||||||
try:
|
|
||||||
@echo {{ project }}
|
|
||||||
@echo {{ repo }}
|
|
||||||
@echo {{ gitlab_image }}
|
|
||||||
@echo {{ etke_image }}
|
|
||||||
|
|
||||||
# show help by default
|
# show help by default
|
||||||
default:
|
default:
|
||||||
@just --list --justfile {{ justfile() }}
|
@just --list --justfile {{ justfile() }}
|
||||||
@@ -22,7 +16,7 @@ update:
|
|||||||
go mod vendor
|
go mod vendor
|
||||||
|
|
||||||
# run linter
|
# run linter
|
||||||
lint: try
|
lint:
|
||||||
golangci-lint run ./...
|
golangci-lint run ./...
|
||||||
|
|
||||||
# automatically fix liter issues
|
# automatically fix liter issues
|
||||||
@@ -30,7 +24,7 @@ lintfix:
|
|||||||
golangci-lint run --fix ./...
|
golangci-lint run --fix ./...
|
||||||
|
|
||||||
# run unit tests
|
# run unit tests
|
||||||
test: try
|
test:
|
||||||
@go test -cover -coverprofile=cover.out -coverpkg=./... -covermode=set ./...
|
@go test -cover -coverprofile=cover.out -coverpkg=./... -covermode=set ./...
|
||||||
@go tool cover -func=cover.out
|
@go tool cover -func=cover.out
|
||||||
-@rm -f cover.out
|
-@rm -f cover.out
|
||||||
@@ -44,10 +38,10 @@ build:
|
|||||||
go build -v -o {{ project }} ./cmd
|
go build -v -o {{ project }} ./cmd
|
||||||
|
|
||||||
# docker login
|
# docker login
|
||||||
login: try
|
login:
|
||||||
@docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
|
@docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
|
||||||
|
|
||||||
# docker build
|
# docker build
|
||||||
docker: try
|
docker:
|
||||||
docker buildx create --use
|
docker buildx create --use
|
||||||
docker buildx build --pull --platform linux/arm64/v8,linux/amd64 --push -t {{ gitlab_image }} -t {{ etke_image }} .
|
docker buildx build --pull --platform linux/arm64/v8,linux/amd64 --push -t {{ gitlab_image }} -t {{ etke_image }} .
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type MailSender interface {
|
type MailSender interface {
|
||||||
Send(from string, to string, data string) error
|
Send(from, to, data string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// SMTP client
|
// SMTP client
|
||||||
@@ -28,7 +28,7 @@ func newClient(cfg *RelayConfig, log *zerolog.Logger) *Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send email
|
// Send email
|
||||||
func (c Client) Send(from string, to string, data string) error {
|
func (c Client) Send(from, to, data string) error {
|
||||||
c.log.Debug().Str("from", from).Str("to", to).Msg("sending email")
|
c.log.Debug().Str("from", from).Str("to", to).Msg("sending email")
|
||||||
|
|
||||||
var conn *smtp.Client
|
var conn *smtp.Client
|
||||||
@@ -67,7 +67,7 @@ func (c Client) Send(from string, to string, data string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createDirectClient connects directly to the provided smtp host
|
// createDirectClient connects directly to the provided smtp host
|
||||||
func (c *Client) createDirectClient(from string, to string) (*smtp.Client, error) {
|
func (c *Client) createDirectClient(from, to string) (*smtp.Client, error) {
|
||||||
localname := strings.SplitN(from, "@", 2)[1]
|
localname := strings.SplitN(from, "@", 2)[1]
|
||||||
target := c.config.Host + ":" + c.config.Port
|
target := c.config.Host + ":" + c.config.Port
|
||||||
conn, err := smtp.Dial(target)
|
conn, err := smtp.Dial(target)
|
||||||
@@ -81,8 +81,8 @@ func (c *Client) createDirectClient(from string, to string) (*smtp.Client, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ok, _ := conn.Extension("STARTTLS"); ok {
|
if ok, _ := conn.Extension("STARTTLS"); ok {
|
||||||
config := &tls.Config{ServerName: c.config.Host}
|
config := &tls.Config{ServerName: c.config.Host} //nolint:gosec // it's smtp, even that is too strict sometimes
|
||||||
conn.StartTLS(config) //nolint:errcheck // if it doesn't work - we can't do anything anyway
|
conn.StartTLS(config) //nolint:errcheck // if it doesn't work - we can't do anything anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.config.Usename != "" {
|
if c.config.Usename != "" {
|
||||||
|
|||||||
@@ -11,24 +11,24 @@ type validatorLoggerWrapper struct {
|
|||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l validatorLoggerWrapper) Info(msg string, args ...interface{}) {
|
func (l validatorLoggerWrapper) Info(msg string, args ...any) {
|
||||||
l.log.Info().Msgf(msg, args...)
|
l.log.Info().Msgf(msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l validatorLoggerWrapper) Error(msg string, args ...interface{}) {
|
func (l validatorLoggerWrapper) Error(msg string, args ...any) {
|
||||||
l.log.Error().Msgf(msg, args...)
|
l.log.Error().Msgf(msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// loggerWrapper is a wrapper around any logger to implement smtp.Logger interface
|
// loggerWrapper is a wrapper around any logger to implement smtp.Logger interface
|
||||||
type loggerWrapper struct {
|
type loggerWrapper struct {
|
||||||
log func(string, ...interface{})
|
log func(string, ...any)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l loggerWrapper) Printf(format string, v ...interface{}) {
|
func (l loggerWrapper) Printf(format string, v ...any) {
|
||||||
l.log(format, v...)
|
l.log(format, v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l loggerWrapper) Println(v ...interface{}) {
|
func (l loggerWrapper) Println(v ...any) {
|
||||||
msg := strings.Repeat("%v ", len(v))
|
msg := strings.Repeat("%v ", len(v))
|
||||||
l.log(msg, v...)
|
l.log(msg, v...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ func NewManager(cfg *Config) *Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s := smtp.NewServer(mailsrv)
|
s := smtp.NewServer(mailsrv)
|
||||||
s.ErrorLog = loggerWrapper{func(s string, i ...interface{}) { cfg.Logger.Error().Msgf(s, i...) }}
|
s.ErrorLog = loggerWrapper{func(s string, i ...any) { cfg.Logger.Error().Msgf(s, i...) }}
|
||||||
s.ReadTimeout = 10 * time.Second
|
s.ReadTimeout = 10 * time.Second
|
||||||
s.WriteTimeout = 10 * time.Second
|
s.WriteTimeout = 10 * time.Second
|
||||||
s.MaxMessageBytes = cfg.MaxSize * 1024 * 1024
|
s.MaxMessageBytes = cfg.MaxSize * 1024 * 1024
|
||||||
@@ -209,7 +209,7 @@ func (m *Manager) loadTLSConfig() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
m.tls.Config = &tls.Config{Certificates: certificates}
|
m.tls.Config = &tls.Config{Certificates: certificates} //nolint:gosec // it's email, even that config is too strict sometimes
|
||||||
m.smtp.TLSConfig = m.tls.Config
|
m.smtp.TLSConfig = m.tls.Config
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,17 +10,28 @@ import (
|
|||||||
"gitlab.com/etke.cc/postmoogle/email"
|
"gitlab.com/etke.cc/postmoogle/email"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NoUserCode SMTP code
|
||||||
|
NoUserCode = 550
|
||||||
|
// BannedCode SMTP code
|
||||||
|
BannedCode = 554
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// NoUserEnhancedCode enhanced SMTP code
|
||||||
|
NoUserEnhancedCode = smtp.EnhancedCode{5, 5, 0}
|
||||||
|
// BannedEnhancedCode enhanced SMTP code
|
||||||
|
BannedEnhancedCode = smtp.EnhancedCode{5, 5, 4}
|
||||||
// ErrBanned returned to banned hosts
|
// ErrBanned returned to banned hosts
|
||||||
ErrBanned = &smtp.SMTPError{
|
ErrBanned = &smtp.SMTPError{
|
||||||
Code: 554,
|
Code: BannedCode,
|
||||||
EnhancedCode: smtp.EnhancedCode{5, 5, 4},
|
EnhancedCode: BannedEnhancedCode,
|
||||||
Message: "please, don't bother me anymore, kupo.",
|
Message: "please, don't bother me anymore, kupo.",
|
||||||
}
|
}
|
||||||
// ErrNoUser returned when no such mailbox found
|
// ErrNoUser returned when no such mailbox found
|
||||||
ErrNoUser = &smtp.SMTPError{
|
ErrNoUser = &smtp.SMTPError{
|
||||||
Code: 550,
|
Code: NoUserCode,
|
||||||
EnhancedCode: smtp.EnhancedCode{5, 5, 0},
|
EnhancedCode: NoUserEnhancedCode,
|
||||||
Message: "no such user here, kupo.",
|
Message: "no such user here, kupo.",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ import (
|
|||||||
"gitlab.com/etke.cc/postmoogle/utils"
|
"gitlab.com/etke.cc/postmoogle/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GraylistCode SMTP code
|
||||||
|
const GraylistCode = 451
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrInvalidEmail for invalid emails :)
|
||||||
|
ErrInvalidEmail = errors.New("please, provide valid email address")
|
||||||
|
// GraylistEnhancedCode is GraylistCode in enhanced code notation
|
||||||
|
GraylistEnhancedCode = smtp.EnhancedCode{4, 5, 1}
|
||||||
|
)
|
||||||
|
|
||||||
// incomingSession represents an SMTP-submission session receiving emails from remote servers
|
// incomingSession represents an SMTP-submission session receiving emails from remote servers
|
||||||
type incomingSession struct {
|
type incomingSession struct {
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
@@ -32,7 +42,7 @@ type incomingSession struct {
|
|||||||
domains []string
|
domains []string
|
||||||
roomID id.RoomID
|
roomID id.RoomID
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context //nolint:containedctx // that's session
|
||||||
addr net.Addr
|
addr net.Addr
|
||||||
tos []string
|
tos []string
|
||||||
from string
|
from string
|
||||||
@@ -89,13 +99,13 @@ func (s *incomingSession) getAddr(envelope *enmime.Envelope) net.Addr {
|
|||||||
return s.addr
|
return s.addr
|
||||||
}
|
}
|
||||||
|
|
||||||
host, portString, _ := net.SplitHostPort(addrHeader) //nolint:errcheck
|
host, portString, _ := net.SplitHostPort(addrHeader) //nolint:errcheck // it is real addr
|
||||||
if host == "" {
|
if host == "" {
|
||||||
return s.addr
|
return s.addr
|
||||||
}
|
}
|
||||||
|
|
||||||
var port int
|
var port int
|
||||||
port, _ = strconv.Atoi(portString) //nolint:errcheck
|
port, _ = strconv.Atoi(portString) //nolint:errcheck // it's a real addr
|
||||||
|
|
||||||
realAddr := &net.TCPAddr{IP: net.ParseIP(host), Port: port}
|
realAddr := &net.TCPAddr{IP: net.ParseIP(host), Port: port}
|
||||||
s.log.Info().Str("addr", realAddr.String()).Msg("real address")
|
s.log.Info().Str("addr", realAddr.String()).Msg("real address")
|
||||||
@@ -115,7 +125,7 @@ func (s *incomingSession) Data(r io.Reader) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
addr := s.getAddr(envelope)
|
addr := s.getAddr(envelope)
|
||||||
reader.Seek(0, io.SeekStart) //nolint:errcheck
|
reader.Seek(0, io.SeekStart) //nolint:errcheck // becase we're sure that's ok
|
||||||
validations := s.getFilters(s.roomID)
|
validations := s.getFilters(s.roomID)
|
||||||
if !validateIncoming(s.from, s.tos[0], addr, s.log, validations) {
|
if !validateIncoming(s.from, s.tos[0], addr, s.log, validations) {
|
||||||
s.ban(addr)
|
s.ban(addr)
|
||||||
@@ -123,8 +133,8 @@ func (s *incomingSession) Data(r io.Reader) error {
|
|||||||
}
|
}
|
||||||
if s.greylisted(addr) {
|
if s.greylisted(addr) {
|
||||||
return &smtp.SMTPError{
|
return &smtp.SMTPError{
|
||||||
Code: 451,
|
Code: GraylistCode,
|
||||||
EnhancedCode: smtp.EnhancedCode{4, 5, 1},
|
EnhancedCode: GraylistEnhancedCode,
|
||||||
Message: "You have been greylisted, try again a bit later.",
|
Message: "You have been greylisted, try again a bit later.",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,16 +174,16 @@ type outgoingSession struct {
|
|||||||
domains []string
|
domains []string
|
||||||
getRoomID func(string) (id.RoomID, bool)
|
getRoomID func(string) (id.RoomID, bool)
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context //nolint:containedctx // that's session
|
||||||
tos []string
|
tos []string
|
||||||
from string
|
from string
|
||||||
fromRoom id.RoomID
|
fromRoom id.RoomID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *outgoingSession) Mail(from string, opts smtp.MailOptions) error {
|
func (s *outgoingSession) Mail(from string, _ smtp.MailOptions) error {
|
||||||
sentry.GetHubFromContext(s.ctx).Scope().SetTag("from", from)
|
sentry.GetHubFromContext(s.ctx).Scope().SetTag("from", from)
|
||||||
if !email.AddressValid(from) {
|
if !email.AddressValid(from) {
|
||||||
return errors.New("please, provide email address")
|
return ErrInvalidEmail
|
||||||
}
|
}
|
||||||
hostname := utils.Hostname(from)
|
hostname := utils.Hostname(from)
|
||||||
var domainok bool
|
var domainok bool
|
||||||
@@ -234,7 +244,7 @@ func validateIncoming(from, to string, senderAddr net.Addr, log *zerolog.Logger,
|
|||||||
case *net.TCPAddr:
|
case *net.TCPAddr:
|
||||||
sender = netaddr.IP
|
sender = netaddr.IP
|
||||||
default:
|
default:
|
||||||
host, _, _ := net.SplitHostPort(senderAddr.String()) // nolint:errcheck
|
host, _, _ := net.SplitHostPort(senderAddr.String()) //nolint:errcheck // interface constraints
|
||||||
sender = net.ParseIP(host)
|
sender = net.ParseIP(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,21 +5,24 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MinSendCommandParts is minimal count of space-separated parts for !pm send command
|
||||||
|
const MinSendCommandParts = 3
|
||||||
|
|
||||||
// ErrInvalidArgs returned when a command's arguments are invalid
|
// ErrInvalidArgs returned when a command's arguments are invalid
|
||||||
var ErrInvalidArgs = fmt.Errorf("invalid arguments")
|
var ErrInvalidArgs = fmt.Errorf("invalid arguments")
|
||||||
|
|
||||||
// ParseSend parses "!pm send" command, returns to, subject, body, err
|
// ParseSend parses "!pm send" command, returns to, subject, body, err
|
||||||
func ParseSend(commandSlice []string) (string, string, string, error) {
|
func ParseSend(commandSlice []string) (to, subject, body string, err error) {
|
||||||
message := strings.Join(commandSlice, " ")
|
message := strings.Join(commandSlice, " ")
|
||||||
lines := strings.Split(message, "\n")
|
lines := strings.Split(message, "\n")
|
||||||
if len(lines) < 3 {
|
if len(lines) < MinSendCommandParts {
|
||||||
return "", "", "", ErrInvalidArgs
|
return "", "", "", ErrInvalidArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
commandSlice = strings.Split(lines[0], " ")
|
commandSlice = strings.Split(lines[0], " ")
|
||||||
to := commandSlice[1]
|
to = commandSlice[1]
|
||||||
subject := lines[1]
|
subject = lines[1]
|
||||||
body := strings.Join(lines[2:], "\n")
|
body = strings.Join(lines[2:], "\n")
|
||||||
|
|
||||||
return to, subject, body, nil
|
return to, subject, body, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ func Hostname(email string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EmailParts parses email address into mailbox, subaddress, and hostname
|
// EmailParts parses email address into mailbox, subaddress, and hostname
|
||||||
func EmailParts(email string) (string, string, string) {
|
func EmailParts(email string) (mailbox, sub, hostname string) {
|
||||||
var mailbox, hostname string
|
|
||||||
address, err := emailaddress.Parse(email)
|
address, err := emailaddress.Parse(email)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
mailbox = address.LocalPart
|
mailbox = address.LocalPart
|
||||||
@@ -44,7 +43,6 @@ func EmailParts(email string) (string, string, string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sub string
|
|
||||||
idx := strings.Index(mailbox, "+")
|
idx := strings.Index(mailbox, "+")
|
||||||
if idx != -1 {
|
if idx != -1 {
|
||||||
sub = strings.ReplaceAll(mailbox[idx:], "+", "")
|
sub = strings.ReplaceAll(mailbox[idx:], "+", "")
|
||||||
@@ -54,7 +52,7 @@ func EmailParts(email string) (string, string, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EmailsList returns human-readable list of mailbox's emails for all available domains
|
// EmailsList returns human-readable list of mailbox's emails for all available domains
|
||||||
func EmailsList(mailbox string, domain string) string {
|
func EmailsList(mailbox, domain string) string {
|
||||||
var msg strings.Builder
|
var msg strings.Builder
|
||||||
domain = SanitizeDomain(domain)
|
domain = SanitizeDomain(domain)
|
||||||
msg.WriteString(mailbox)
|
msg.WriteString(mailbox)
|
||||||
|
|||||||
Reference in New Issue
Block a user