9 Commits

Author SHA1 Message Date
Aine
12d2fee2d4 fix cc handling 2023-02-06 15:53:16 +02:00
Aine
ddf2460dbd fix dequeue account data issue 2023-01-27 00:02:23 +02:00
Aine
3f1fd00fb6 fix file uploads from incoming emails into threads 2023-01-24 16:34:31 +02:00
Aine
ac9c27aa32 handle multiple emails in header 'To' 2023-01-09 16:23:54 +02:00
Aine
1e9558c1fc registry dual writes 2023-01-08 14:23:56 +02:00
Aine
174930fc90 allow only text message events for commands 2023-01-08 00:58:14 +02:00
Aine
0559978fa2 log level changes 2023-01-04 11:22:50 +02:00
Aine
f54b87c1f7 resync rooms every 5 minutes 2023-01-03 20:13:30 +02:00
Aine
2ac6c64d13 make banlist consistent, fixes #54 2022-12-14 00:35:15 +02:00
13 changed files with 78 additions and 25 deletions

View File

@@ -1,6 +1,7 @@
### CI vars
CI_LOGIN_COMMAND = @echo "Not a CI, skip login"
CI_REGISTRY_IMAGE ?= registry.gitlab.com/etke.cc/postmoogle
REGISTRY_IMAGE ?= registry.etke.cc/etke.cc/postmoogle
CI_COMMIT_TAG ?= latest
# for main branch it must be set explicitly
ifeq ($(CI_COMMIT_TAG), main)
@@ -51,4 +52,4 @@ login:
# docker build
docker:
docker buildx create --use
docker buildx build --platform linux/arm64/v8,linux/amd64 --push -t ${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG} .
docker buildx build --platform linux/arm64/v8,linux/amd64 --push -t ${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG} -t ${REGISTRY_IMAGE}:${CI_COMMIT_TAG} .

View File

@@ -119,12 +119,14 @@ func (b *Bot) IsTrusted(addr net.Addr) bool {
}
}
b.log.Debug("address %s is NOT trusted", ip)
return false
}
// Ban an address
func (b *Bot) Ban(addr net.Addr) {
if !b.cfg.BanlistEnalbed() {
return
}
if b.IsTrusted(addr) {
return
}

View File

@@ -6,6 +6,7 @@ import (
"strings"
"time"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/format"
"maunium.net/go/mautrix/id"
@@ -284,6 +285,10 @@ func (b *Bot) handle(ctx context.Context) {
b.Error(ctx, evt.RoomID, "cannot read message")
return
}
// ignore any type apart from text (e.g. reactions, redactions, notices, etc)
if content.MsgType != event.MsgText {
return
}
message := strings.TrimSpace(content.Body)
commandSlice := b.parseCommand(message, true)
if commandSlice == nil {

View File

@@ -1,8 +1,6 @@
package config
import (
"fmt"
"gitlab.com/etke.cc/go/logger"
"gitlab.com/etke.cc/linkpearl"
"maunium.net/go/mautrix/id"
@@ -32,15 +30,24 @@ func New(lp *linkpearl.Linkpearl, log *logger.Logger) *Manager {
return m
}
// BanlistEnalbed or not
func (m *Manager) BanlistEnalbed() bool {
return m.ble
}
// GetBot config
func (m *Manager) GetBot() Bot {
config, err := m.lp.GetAccountData(acBotKey)
var err error
var config Bot
config, err = m.lp.GetAccountData(acBotKey)
if err != nil {
m.log.Error("cannot get bot settings: %v", utils.UnwrapError(err))
}
if config == nil {
config = make(Bot, 0)
return config
}
m.ble = config.BanlistEnabled()
return config
}
@@ -80,6 +87,7 @@ func (m *Manager) GetBanlist() List {
}
if config == nil {
config = make(List, 0)
return config
}
m.bl = config
return config
@@ -88,7 +96,7 @@ func (m *Manager) GetBanlist() List {
// SetBanlist config
func (m *Manager) SetBanlist(cfg List) error {
if !m.ble {
return fmt.Errorf("banlist is disabled, kupo")
return nil
}
m.mu.Lock("banlist")
@@ -109,6 +117,7 @@ func (m *Manager) GetGreylist() List {
}
if config == nil {
config = make(List, 0)
return config
}
return config

View File

@@ -38,9 +38,11 @@ func (b *Bot) migrate() error {
}
func (b *Bot) syncRooms() error {
adminRooms := []id.RoomID{}
adminRoom := b.cfg.GetBot().AdminRoom()
if adminRoom != "" {
b.adminRooms = append(b.adminRooms, adminRoom)
adminRooms = append(adminRooms, adminRoom)
}
resp, err := b.lp.GetClient().JoinedRooms()
@@ -60,9 +62,10 @@ func (b *Bot) syncRooms() error {
}
if cfg.Owner() != "" && b.allowAdmin(id.UserID(cfg.Owner()), "") {
b.adminRooms = append(b.adminRooms, roomID)
adminRooms = append(adminRooms, roomID)
}
}
b.adminRooms = adminRooms
return nil
}
@@ -101,3 +104,8 @@ func (b *Bot) initBotUsers() ([]string, error) {
cfg.Set(config.BotUsers, "@*:"+homeserver)
return cfg.Users(), b.cfg.SetBot(cfg)
}
// SyncRooms and mailboxes
func (b *Bot) SyncRooms() {
b.syncRooms() //nolint:errcheck // nothing can be done here
}

View File

@@ -44,7 +44,7 @@ func (b *Bot) Sendmail(eventID id.EventID, from, to, data string) (bool, error)
err := b.sendmail(from, to, data)
if err != nil {
if strings.HasPrefix(err.Error(), "4") {
b.log.Debug("email %s (from=%s to=%s) was added to the queue: %v", eventID, from, to, err)
b.log.Info("email %s (from=%s to=%s) was added to the queue: %v", eventID, from, to, err)
return true, b.q.Add(eventID.String(), from, to, data)
}
return false, err
@@ -128,7 +128,6 @@ func (b *Bot) IncomingEmail(ctx context.Context, email *email.Email) error {
b.setThreadID(roomID, email.MessageID, threadID)
b.setLastEventID(roomID, threadID, eventID)
threadID = eventID
if !cfg.NoFiles() {
b.sendFiles(ctx, roomID, email.Files, cfg.NoThreads(), threadID)
@@ -179,7 +178,7 @@ func (b *Bot) SendEmailReply(ctx context.Context) {
meta.MessageID = email.MessageID(evt.ID, meta.FromDomain)
meta.References = meta.References + " " + meta.MessageID
b.log.Debug("send email reply: %+v", meta)
b.log.Info("sending email reply: %+v", meta)
eml := email.New(meta.MessageID, meta.InReplyTo, meta.References, meta.Subject, meta.From, meta.To, meta.RcptTo, meta.CC, body, htmlBody, nil)
data := eml.Compose(b.cfg.GetBot().DKIMPrivateKey())
if data == "" {
@@ -189,7 +188,7 @@ func (b *Bot) SendEmailReply(ctx context.Context) {
var queued bool
var hasErr bool
recipients := meta.Recipients()
recipients := meta.Recipients
for _, to := range recipients {
queued, err = b.Sendmail(evt.ID, meta.From, to, data)
if queued {
@@ -222,6 +221,7 @@ type parentEmail struct {
InReplyTo string
References string
Subject string
Recipients []string
}
// fixtofrom attempts to "fix" or rather reverse the To, From and CC headers
@@ -229,7 +229,7 @@ type parentEmail struct {
// that will be sent from postmoogle.
// To do so, we need to reverse From and To headers, but Cc should be adjusted as well,
// thus that hacky workaround below:
func (e *parentEmail) fixtofrom(newSenderMailbox string, domains []string) {
func (e *parentEmail) fixtofrom(newSenderMailbox string, domains []string) string {
newSenders := make(map[string]string, len(domains))
for _, domain := range domains {
sender := newSenderMailbox + "@" + domain
@@ -271,11 +271,28 @@ func (e *parentEmail) fixtofrom(newSenderMailbox string, domains []string) {
e.CC = strings.ReplaceAll(e.CC, newSender, originalFrom)
}
}
return previousSender
}
// Recipients returns list of recipients (to, cc)
func (e parentEmail) Recipients() []string {
return append(email.AddressList(e.CC), e.To)
func (e *parentEmail) calculateRecipients(from string) {
recipients := map[string]struct{}{}
recipients[e.From] = struct{}{}
for _, addr := range strings.Split(email.Address(e.To), ",") {
recipients[addr] = struct{}{}
}
for _, addr := range email.AddressList(e.CC) {
recipients[addr] = struct{}{}
}
delete(recipients, from)
rcpts := make([]string, 0, len(recipients))
for rcpt := range recipients {
rcpts = append(rcpts, rcpt)
}
e.Recipients = rcpts
}
func (b *Bot) getParentEvent(evt *event.Event) (id.EventID, *event.Event) {
@@ -331,7 +348,8 @@ func (b *Bot) getParentEmail(evt *event.Event, newFromMailbox string) *parentEma
parent.RcptTo = utils.EventField[string](&parentEvt.Content, eventRcptToKey)
parent.InReplyTo = utils.EventField[string](&parentEvt.Content, eventMessageIDkey)
parent.References = utils.EventField[string](&parentEvt.Content, eventReferencesKey)
parent.fixtofrom(newFromMailbox, b.domains)
senderEmail := parent.fixtofrom(newFromMailbox, b.domains)
parent.calculateRecipients(senderEmail)
parent.MessageID = email.MessageID(parentEvt.ID, parent.FromDomain)
if parent.InReplyTo == "" {
parent.InReplyTo = parent.MessageID

View File

@@ -67,7 +67,7 @@ func (q *Queue) Process() {
return
}
if dequeue := q.try(itemkey, maxRetries); dequeue {
q.log.Debug("email %q has been delivered", id)
q.log.Info("email %q has been delivered", id)
err = q.Remove(id)
if err != nil {
q.log.Error("cannot dequeue email %q: %v", id, err)

View File

@@ -60,7 +60,7 @@ func (q *Queue) Remove(id string) error {
q.mu.Lock(itemkey)
defer q.mu.Unlock(itemkey)
return q.lp.SetAccountData(itemkey, nil)
return q.lp.SetAccountData(itemkey, map[string]string{})
}
// try to send email
@@ -85,11 +85,11 @@ func (q *Queue) try(itemkey string, maxRetries int) bool {
err = q.sendmail(item["from"], item["to"], item["data"])
if err == nil {
q.log.Debug("email %q from queue was delivered")
q.log.Info("email %q from queue was delivered")
return true
}
q.log.Debug("attempted to deliver email id=%q, retry=%q, but it's not ready yet: %v", item["id"], item["attempts"], err)
q.log.Info("attempted to deliver email id=%q, retry=%q, but it's not ready yet: %v", item["id"], item["attempts"], err)
attempts++
item["attempts"] = strconv.Itoa(attempts)
err = q.lp.SetAccountData(itemkey, item)

View File

@@ -153,6 +153,11 @@ func initCron() {
if err != nil {
log.Error("cannot start queue processing cronjob: %v", err)
}
err = cron.AddJob("*/5 * * * *", mxb.SyncRooms)
if err != nil {
log.Error("cannot start sync rooms cronjob: %v", err)
}
}
func initShutdown(quit chan struct{}) {

View File

@@ -41,7 +41,7 @@ func New(messageID, inReplyTo, references, subject, from, to, rcptto, cc, text,
From: Address(from),
To: Address(to),
CC: AddressList(cc),
RcptTo: rcptto,
RcptTo: Address(rcptto),
Subject: subject,
Text: text,
HTML: html,

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"net/mail"
"regexp"
"strings"
"time"
"maunium.net/go/mautrix/id"
@@ -26,6 +27,10 @@ func MessageID(eventID id.EventID, domain string) string {
func Address(email string) string {
addr, _ := mail.ParseAddress(email) //nolint:errcheck // if it fails here, nothing will help
if addr == nil {
list := AddressList(email)
if len(list) > 0 {
return strings.Join(list, ",")
}
return email
}

View File

@@ -42,7 +42,7 @@ func (l *Listener) Accept() (net.Conn, error) {
continue
}
l.log.Debug("accepted connection from %q", conn.RemoteAddr())
l.log.Info("accepted connection from %q", conn.RemoteAddr())
return conn, nil
}
}

View File

@@ -80,8 +80,8 @@ func NewManager(cfg *Config) *Manager {
if len(cfg.Domains) == 1 {
s.Domain = cfg.Domains[0]
}
if log.GetLevel() == "DEBUG" || log.GetLevel() == "TRACE" {
s.Debug = loggerWriter{func(s string) { log.Debug(s) }}
if log.GetLevel() == "INFO" || log.GetLevel() == "DEBUG" || log.GetLevel() == "TRACE" {
s.Debug = loggerWriter{func(s string) { log.Info(s) }}
}
m := &Manager{