diff --git a/README.md b/README.md index abd8417..e1fe8a4 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,13 @@ An Email to Matrix bridge ## Features / Roadmap / TODO +- [ ] **BUG**: no auto invites - [x] SMTP server - [ ] SMTP client - [x] Matrix bot - [x] Configuration in room's account data -- [ ] Receive emails to matrix rooms +- [x] Receive emails to matrix rooms +- [ ] Receive attachments - [ ] Map email threads to matrix threads - [ ] Reply to matrix thread sends reply into email thread - [ ] Send a message to matrix room with special format to send a new email @@ -34,3 +36,14 @@ env vars * **POSTMOOGLE_DB_DIALECT** - database dialect (postgres, sqlite3) You can find default values in [config/defaults.go](config/defaults.go) + +## Development + +### Prerequisites + +* [ssmtp](https://wiki.archlinux.org/title/SSMTP) +* configured mailbox `test@localhost` + +### Testing + +Run `send` script of the `e2e` dir with example emails diff --git a/bot/bot.go b/bot/bot.go index 8c634e5..13aa450 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -10,6 +10,7 @@ import ( "github.com/getsentry/sentry-go" "gitlab.com/etke.cc/go/logger" "gitlab.com/etke.cc/linkpearl" + "gitlab.com/etke.cc/postmoogle/utils" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/format" "maunium.net/go/mautrix/id" @@ -28,10 +29,11 @@ type Bot struct { // New creates a new matrix bot func New(lp *linkpearl.Linkpearl, log *logger.Logger, prefix, domain string) *Bot { return &Bot{ - prefix: prefix, - domain: domain, - log: log, - lp: lp, + roomsmu: &sync.Mutex{}, + prefix: prefix, + domain: domain, + log: log, + lp: lp, } } @@ -73,7 +75,7 @@ func (b *Bot) Start() error { // Send email to matrix room func (b *Bot) Send(from, to, subject, body string) error { - roomID, ok := b.rooms[to] + roomID, ok := b.rooms[utils.Mailbox(to)] if !ok || roomID == "" { return errors.New("room not found") } diff --git a/bot/command.go b/bot/command.go index a910ce2..63bc5ad 100644 --- a/bot/command.go +++ b/bot/command.go @@ -33,7 +33,7 @@ func (b *Bot) parseCommand(message string) []string { return nil } - message = strings.Replace(message, b.prefix+" ", "", 1) + message = strings.TrimSpace(strings.Replace(message, b.prefix, "", 1)) return strings.Split(message, " ") } diff --git a/bot/data.go b/bot/data.go index 060ae95..ed99f20 100644 --- a/bot/data.go +++ b/bot/data.go @@ -58,5 +58,6 @@ func (b *Bot) getSettings(ctx context.Context, roomID id.RoomID) (*settings, err func (b *Bot) setSettings(ctx context.Context, roomID id.RoomID, cfg *settings) error { span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("setSettings")) defer span.Finish() + return b.lp.GetClient().SetRoomAccountData(roomID, settingskey, cfg) } diff --git a/bot/mailbox.go b/bot/mailbox.go index d92cf75..979d76d 100644 --- a/bot/mailbox.go +++ b/bot/mailbox.go @@ -4,6 +4,7 @@ import ( "context" "github.com/getsentry/sentry-go" + "gitlab.com/etke.cc/postmoogle/utils" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/format" "maunium.net/go/mautrix/id" @@ -23,7 +24,7 @@ func (b *Bot) syncRooms(ctx context.Context) error { for _, roomID := range resp.JoinedRooms { cfg, serr := b.getSettings(span.Context(), roomID) if serr != nil { - b.Error(span.Context(), roomID, "cannot get room settings: %v", err) + b.log.Warn("cannot get %s settings: %v", roomID, err) continue } if cfg.Mailbox != "" { @@ -48,8 +49,7 @@ func (b *Bot) getMailbox(ctx context.Context, evt *event.Event) { cfg, err := b.getSettings(span.Context(), evt.RoomID) if err != nil || cfg == nil { - b.Error(span.Context(), evt.RoomID, "cannot get settings: %v", err) - return + b.log.Warn("cannot get %s settings: %v", evt.RoomID, err) } if cfg.Mailbox == "" { @@ -69,6 +69,7 @@ func (b *Bot) setMailbox(ctx context.Context, evt *event.Event, mailbox string) span := sentry.StartSpan(ctx, "http.server", sentry.TransactionName("setMailbox")) defer span.Finish() + mailbox = utils.Mailbox(mailbox) existingID, ok := b.rooms[mailbox] if ok && existingID != "" && existingID != evt.RoomID { content := format.RenderMarkdown("Mailbox "+mailbox+"@"+b.domain+" already taken", true, true) diff --git a/config/defaults.go b/config/defaults.go index 559d384..1489f84 100644 --- a/config/defaults.go +++ b/config/defaults.go @@ -6,7 +6,7 @@ var defaultConfig = &Config{ Port: "25", Prefix: "!pm", DB: DB{ - DSN: "/tmp/postmoogle.db", + DSN: "local.db", Dialect: "sqlite3", }, Sentry: Sentry{ diff --git a/e2e/send b/e2e/send new file mode 100755 index 0000000..2aeeab9 --- /dev/null +++ b/e2e/send @@ -0,0 +1,3 @@ +#!/bin/bash + +ssmtp -v test@localhost < $1 diff --git a/e2e/simple.eml b/e2e/simple.eml new file mode 100644 index 0000000..8d4f24a --- /dev/null +++ b/e2e/simple.eml @@ -0,0 +1,54 @@ +From: James Hillyerd +Content-Type: multipart/alternative; boundary="Apple-Mail=_E091454E-BCFA-43B4-99C0-678AEC9868D6" +Subject: MIME test 1 +Date: Sat, 13 Oct 2012 15:33:07 -0700 +Message-Id: <4E2E5A48-1A2C-4450-8663-D41B451DA93E@makita.skynet> +To: test@localhost +Mime-Version: 1.0 (Apple Message framework v1283) +X-Mailer: Apple Mail (2.1283) + + +--Apple-Mail=_E091454E-BCFA-43B4-99C0-678AEC9868D6 +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +Test of text section +--Apple-Mail=_E091454E-BCFA-43B4-99C0-678AEC9868D6 +Content-Type: multipart/related; + type="text/html"; + boundary="Apple-Mail=_D2ABE25A-F0FE-404E-94EE-D98BD23448D5" + + +--Apple-Mail=_D2ABE25A-F0FE-404E-94EE-D98BD23448D5 +Content-Transfer-Encoding: 7bit +Content-Type: text/html; + charset=us-ascii + +Test of HTML section +--Apple-Mail=_D2ABE25A-F0FE-404E-94EE-D98BD23448D5 +Content-Transfer-Encoding: base64 +Content-Disposition: inline; + filename=favicon.png +Content-Type: image/png; + x-unix-mode=0644; + name="favicon.png" +Content-Id: <8B8481A2-25CA-4886-9B5A-8EB9115DD064@skynet> + +iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ +bWFnZVJlYWR5ccllPAAAAlFJREFUeNqUU8tOFEEUPVVdNV3dPe8xYRBnjGhmBgKjKzCIiQvBoIaN +bly5Z+PSv3Aj7DSiP2B0rwkLGVdGgxITSCRIJGSMEQWZR3eVt5sEFBgTb/dN1yvnnHtPNTPG4Pqd +HgCMXnPRSZrpSuH8vUJu4DE4rYHDGAZDX62BZttHqTiIayM3gGiXQsgYLEvATaqxU+dy1U13YXap +XptpNHY8iwn8KyIAzm1KBdtRZWErpI5lEWTXp5Z/vHpZ3/wyKKwYGGOdAYwR0EZwoezTYApBEIOb +yELl/aE1/83cp40Pt5mxqCKrE4Ck+mVWKKcI5tA8BLEhRBKJLjez6a7MLq7XZtp+yyOawwCBtkiB +VZDKzRk4NN7NQBMYPHiZDFhXY+p9ff7F961vVcnl4R5I2ykJ5XFN7Ab7Gc61VoipNBKF+PDyztu5 +lfrSLT/wIwCxq0CAGtXHZTzqR2jtwQiXONma6hHpj9sLT7YaPxfTXuZdBGA02Wi7FS48YiTfj+i2 +NhqtdhP5RC8mh2/Op7y0v6eAcWVLFT8D7kWX5S9mepp+C450MV6aWL1cGnvkxbwHtLW2B9AOkLeU +d9KEDuh9fl/7CEj7YH5g+3r/lWfF9In7tPz6T4IIwBJOr1SJyIGQMZQbsh5P9uBq5VJtqHh2mo49 +pdw5WFoEwKWqWHacaWOjQXWGcifKo6vj5RGS6zykI587XeUIQDqJSmAp+lE4qt19W5P9o8+Lma5D +cjsC8JiT607lMVkdqQ0Vyh3lHhmh52tfNy78ajXv0rgYzv8nfwswANuk+7sD/Q0aAAAAAElFTkSu +QmCC + +--Apple-Mail=_D2ABE25A-F0FE-404E-94EE-D98BD23448D5-- + +--Apple-Mail=_E091454E-BCFA-43B4-99C0-678AEC9868D6-- diff --git a/e2e/uptimerobot.eml b/e2e/uptimerobot.eml new file mode 100644 index 0000000..34f6f7a --- /dev/null +++ b/e2e/uptimerobot.eml @@ -0,0 +1,268 @@ +Return-Path: +Delivered-To: test@localhost +Received: from mp12.migadu.com ([2001:41d0:8:6d80::]) + (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) + by ms2.migadu.com with LMTPS + id AFCBC0JA/2IjvAAAThjiIw + (envelope-from ) + for ; Fri, 19 Aug 2022 09:48:18 +0200 +Received: from aspmx1.migadu.com ([2001:41d0:8:6d80::]) + (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) + by mp12.migadu.com with LMTPS + id AGRUC0JA/2KpWQAAauVa8A + (envelope-from ) + for ; Fri, 19 Aug 2022 09:48:18 +0200 +Received: from mail2.uptimerobot.com (mail2.uptimerobot.com [216.144.250.150]) + (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) + (No client certificate requested) + by aspmx1.migadu.com (Postfix) with ESMTPS id 892E821C17 + for ; Fri, 19 Aug 2022 09:48:17 +0200 (CEST) +dkim-signature:v=1; c=relaxed/relaxed; h=content-type:from:to:subject:message-id:content-transfer-encoding:date:mime-version; + d=uptimerobot.com; s=mail2; a=rsa-sha256; + bh=wgbGkwTvf0iMf6GWyq4eRri7/0akAM3yIzjn52Vvm6M=; + b=oeLExxHN3oEy4uoeSTxwDqFfC/xKOCtY7AY1OKR9P3AoV3Pw+iu8rNeAjDh9ySL5p + FibtVl9XjOyECSQpumHo27sqVaR0IO/bqBxlJRxfBw07gA/q1JmlDtryQeaV1vM3s7p + t2Rs2fdYdw2s2RYBEjP+6SqtL6vUq1rUSB+4YRtLk1cuiXIjDa3vYuuT2NioIxEo2dk + oUuvPW8jRXdE2gnmdUSZ42Hy8uX7Wr9ScAo8j/xNiFTWDPqD/u9IpHMDuVlKDfWhEgm + 1YoT9ZDcJJ13JZN29ugY4u1tV+vu8k3NfLnPptqUif0S+b0lIPKTcu1af4nvZmjdEFZ + rbt3Rn4Og==; +Received: from [127.0.0.1] ([52.70.84.165]) by uptimerobot.com with + MailEnable ESMTPA; Fri, 19 Aug 2022 07:48:20 +0000 +Content-Type: text/html; charset=utf-8 +From: UptimeRobot +To: test@localhost +Subject: Monitor is UP: Buscarron +Message-ID: <1660895293792.132971@uptimerobot.com> +Content-Transfer-Encoding: quoted-printable +Date: Fri, 19 Aug 2022 07:48:13 +0000 +MIME-Version: 1.0 +X-Migadu-Flow: FLOW_IN +X-Migadu-To: test@localhost +X-Migadu-Country: US +ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=etke.cc; + s=key1; t=1660895298; + h=from:from:reply-to:subject:subject:date:date:message-id:message-id: + to:to:cc:mime-version:mime-version:content-type:content-type: + content-transfer-encoding:content-transfer-encoding:dkim-signature; + bh=wgbGkwTvf0iMf6GWyq4eRri7/0akAM3yIzjn52Vvm6M=; + b=e6P3Be4Nf/cfOFkwt4r94xgp1JzsnT8WANfGuRU7VOfioRm6VHzpYOFJWlFXqwhxspk2lo + m6GaPjyQtJrSiYkcQWtUb9K/K2Y6r7JEb9IziyIvqzKQ2l/K5OPwX/nA2mDKYuTe4PR2/V + /Ec/zBKosuHKuBsyw35mFLF3uLJHyddpuYRdLhIM6qeksXC4bXQek1UtWSQ/P11MwFZ5DM + bSKU20djOMjYu0qE7xXa7/Zj78SnL08VKFLjo5OAboeDZZYfpdMh5Z0YWGPt4QkZWi9AON + fa49qfUYJwai1RVxKpuNhync6KjKv+8ceSgw+8ySwz85xwyKSIae9ZZ8NekouA== +ARC-Seal: i=1; s=key1; d=etke.cc; t=1660895298; a=rsa-sha256; cv=none; + b=FgLD30AkdygNHpzdD9OdIjqeCDr/dOxB0bsn8zk9Et5Ips1OmxbZIAs/bK71BAc3vefpnk + kxm63eFhABQZoI8TjoOtwE2lW2wOSmaoovFxUOqCSh7RO2mXD3wLX9OGeqbP+cTNdaYTab + lkClWxCQ2+mRABQw7YPHVt81IVcC2FzOBx8IXIlJrHTJ7C9J+yLRXIXqoZ0un0n6msASnh + q7nsXWc+U6M8aGTPHUVm5ur3muqIIaLBSHkDHfCjIAAxOObw0P01VhpjE4EiWM8d9hch7+ + f8efT+7d/BwnIQy4C5gnhyAMtzaFN52MDlFtGAo/1Vba9yuByaf+1f8umwChSg== +ARC-Authentication-Results: i=1; + aspmx1.migadu.com; + dkim=pass header.d=uptimerobot.com header.s=mail2 header.b=oeLExxHN; + dmarc=pass (policy=quarantine) header.from=uptimerobot.com; + spf=pass (aspmx1.migadu.com: domain of alert@uptimerobot.com designates 216.144.250.150 as permitted sender) smtp.mailfrom=alert@uptimerobot.com +X-Migadu-Spam-Score: -7.21 +Authentication-Results: aspmx1.migadu.com; + dkim=pass header.d=uptimerobot.com header.s=mail2 header.b=oeLExxHN; + dmarc=pass (policy=quarantine) header.from=uptimerobot.com; + spf=pass (aspmx1.migadu.com: domain of alert@uptimerobot.com designates 216.144.250.150 as permitted sender) smtp.mailfrom=alert@uptimerobot.com +X-Migadu-Queue-Id: 892E821C17 +X-Spam-Score: -7.21 +X-Migadu-Scanner: scn0.migadu.com + + + + + + Monitor is UP = +: Buscarron + + + +
+
+
+ + + + + + + +
+ + + Go to monitor → +
+
+

+ Buscarron is up. +

+
+
+
+
+ +
+
+

Hello etke.cc,

+

The latest incident has = +been resolved and your monitor is up again. Good job!

+
+ +
+ +
Monitor name
+

Buscarron

+
+ +
Checked URL
+

+ https://buscarron.etke.cc/_health

+
+ +
Root cause
+

+ HTTP 502 - Bad = +Gateway +

+
+ +
Incident started at
+

2022-08-19 10:43:14

+
+ +
Resolved = +at
+

2022-08-19 10:48:13

+
+ +
Duration
+

4 minutes and 59 seconds

+ +
+ + + + + +
+ + + = + + +
+ View incident details + = +
+
+ Comment = +incident +
+
+ +
+
+ +
+ + + + +
+

+ TIP: Resolve incidents, write a post mortem, rinse and = +repeat! +

+

+ Comment the incidents, so you can analyze what happened= +, when and why, to overcome the issues in the future. Find out more +

+ +
+
+
+ This email was sent by + UptimeRobot. + + You can update your = +alert preferences in the dashboard. +
+
+
+ + + diff --git a/go.mod b/go.mod index 7370d07..5cef23e 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( github.com/emersion/go-smtp v0.15.0 github.com/getsentry/sentry-go v0.13.0 + github.com/jhillyerd/enmime v0.10.0 github.com/lib/pq v1.10.6 github.com/mattn/go-sqlite3 v1.14.14 gitlab.com/etke.cc/go/env v1.0.0 @@ -14,13 +15,21 @@ require ( ) require ( + github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect + github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-runewidth v0.0.12 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/rs/zerolog v1.27.0 // indirect + github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/tidwall/gjson v1.14.3 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect @@ -29,6 +38,7 @@ require ( golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect + golang.org/x/text v0.3.7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect maunium.net/go/maulogger/v2 v2.3.2 // indirect ) diff --git a/go.sum b/go.sum index 237c497..1cc54e5 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,8 @@ github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI= +github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ= github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= @@ -8,13 +11,21 @@ github.com/emersion/go-smtp v0.15.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVR github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo= github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= +github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 h1:gBeyun7mySAKWg7Fb0GOcv0upX9bdaZScs8QcRo8mEY= +github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc= +github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= +github.com/jhillyerd/enmime v0.10.0 h1:DZEzhptPRBesvN3gf7K1BOh4rfpqdsdrEoxW1Edr/3s= +github.com/jhillyerd/enmime v0.10.0/go.mod h1:Qpe8EEemJMFAF8+NZoWdpXvK2Yb9dRF0k/z6mkcDHsA= github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -23,15 +34,28 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs= github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= +github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= +github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= @@ -52,15 +76,24 @@ gitlab.com/etke.cc/linkpearl v0.0.0-20220816155856-8f4cd5cce574 h1:9uWcP8vME9gPJ gitlab.com/etke.cc/linkpearl v0.0.0-20220816155856-8f4cd5cce574/go.mod h1:CqwzwxVogKG6gDWTPTen3NyWbTESg42jxoTfXXwDGKQ= golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c= golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20210501142056-aec3718b3fa0/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= maunium.net/go/maulogger/v2 v2.3.2 h1:1XmIYmMd3PoQfp9J+PaHhpt80zpfmMqaShzUTC7FwY0= diff --git a/smtp/server.go b/smtp/server.go index 61dd96b..61a1df9 100644 --- a/smtp/server.go +++ b/smtp/server.go @@ -2,6 +2,7 @@ package smtp import ( "context" + "os" "time" "github.com/emersion/go-smtp" @@ -46,6 +47,9 @@ func Start(domain, port, loglevel string, client Client) error { s.ReadTimeout = 10 * time.Second s.WriteTimeout = 10 * time.Second s.MaxMessageBytes = 63 * 1024 + if log.GetLevel() == "DEBUG" || log.GetLevel() == "TRACE" { + s.Debug = os.Stdout + } log.Info("Starting SMTP server on %s:%s", domain, port) return s.ListenAndServe() diff --git a/smtp/session.go b/smtp/session.go index a5856c9..0bdc22f 100644 --- a/smtp/session.go +++ b/smtp/session.go @@ -6,7 +6,9 @@ import ( "github.com/emersion/go-smtp" "github.com/getsentry/sentry-go" + "github.com/jhillyerd/enmime" "gitlab.com/etke.cc/go/logger" + "gitlab.com/etke.cc/postmoogle/utils" ) type session struct { @@ -33,13 +35,14 @@ func (s *session) Rcpt(to string) error { s.log.Error("cannot get mappings: %v", err) return err } - _, ok := mappings[to] + s.log.Debug("mappings: %v", mappings) + _, ok := mappings[utils.Mailbox(to)] if !ok { s.log.Debug("mapping for %s not found", to) return smtp.ErrAuthRequired } - if Domain(to) != s.domain { + if utils.Hostname(to) != s.domain { s.log.Debug("wrong domain of %s", to) return smtp.ErrAuthRequired } @@ -50,12 +53,16 @@ func (s *session) Rcpt(to string) error { } func (s *session) Data(r io.Reader) error { - b, err := io.ReadAll(r) + parser := enmime.NewParser() + env, err := parser.ReadEnvelope(r) if err != nil { - s.log.Error("cannot read data: %v", err) return err } - return s.client.Send(s.from, s.to, "", string(b)) + text := env.Text + if env.HTML != "" { + text = env.HTML + } + return s.client.Send(s.from, s.to, env.GetHeader("Subject"), text) } func (s *session) Reset() {} diff --git a/smtp/smtp.go b/smtp/smtp.go index f89e232..5ebe1ce 100644 --- a/smtp/smtp.go +++ b/smtp/smtp.go @@ -9,5 +9,5 @@ import ( // Client interface to send emails type Client interface { GetMappings(context.Context) (map[string]id.RoomID, error) - Send(from, to, subject, body string) error + Send(from, mailbox, subject, body string) error } diff --git a/smtp/utils.go b/smtp/utils.go deleted file mode 100644 index 010e5b3..0000000 --- a/smtp/utils.go +++ /dev/null @@ -1,7 +0,0 @@ -package smtp - -import "strings" - -func Domain(email string) string { - return email[strings.LastIndex(email, "@")+1:] -} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..79afa3f --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,15 @@ +package utils + +import "strings" + +func Mailbox(email string) string { + index := strings.LastIndex(email, "@") + if index == -1 { + return email + } + return email[:strings.LastIndex(email, "@")] +} + +func Hostname(email string) string { + return email[strings.LastIndex(email, "@")+1:] +}