From 369115d78169b123a14d9c6d322ca06b6664e8b2 Mon Sep 17 00:00:00 2001 From: Guilherme Rugai Freire Date: Sun, 18 Feb 2024 01:26:53 -0300 Subject: [PATCH] initial commit --- .gitignore | 2 + README.md | 3 + cmd/mail_server/main.go | 129 ++++++++++++++++++++++++++++++++++++++++ go.mod | 9 +++ go.sum | 6 ++ migration.sql | 12 ++++ 6 files changed, 161 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 cmd/mail_server/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 migration.sql diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e96fcf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +main +*.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..364086e --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Nothing Mail + +A temporary email service diff --git a/cmd/mail_server/main.go b/cmd/mail_server/main.go new file mode 100644 index 0000000..d9de63e --- /dev/null +++ b/cmd/mail_server/main.go @@ -0,0 +1,129 @@ +package main + +import ( + "database/sql" + "io" + "log" + "time" + + "github.com/emersion/go-smtp" + _ "github.com/mattn/go-sqlite3" +) + +type Backend struct { + db *sql.DB +} + +func (backend *Backend) NewSession(c *smtp.Conn) (smtp.Session, error) { + tx, err := backend.db.Begin() + if err != nil { + return nil, err + } + + return &Session{ + tx: tx, + }, nil +} + +type Session struct { + tx *sql.Tx + from, rcpt string + rcpt_addr_id int64 +} + +func (session *Session) AuthPlain(username, password string) error { + return nil +} + +func (session *Session) Mail(from string, opts *smtp.MailOptions) error { + session.from = from + return nil +} + +func (session *Session) Rcpt(to string, opts *smtp.RcptOptions) error { + session.rcpt = to + + stmt, err := session.tx.Prepare("INSERT OR IGNORE INTO inboxes (addr) VALUES (?) RETURNING id") + if err != nil { + return err + } + defer stmt.Close() + + res, err := stmt.Exec(to) + if err != nil { + return err + } + + // sometimes does not work and returns 0 + // see https://github.com/mattn/go-sqlite3/issues/1140 + id, err := res.LastInsertId() + if err != nil { + return err + } + + log.Println("Last id: ", id) + + session.rcpt_addr_id = id + + return nil +} + +func (session *Session) Data(reader io.Reader) error { + if bytes, err := io.ReadAll(reader); err != nil { + return err + } else { + + stmt, err := session.tx.Prepare("INSERT INTO mails (inbox_id, from_addr, data) VALUES (?, ?, ?)") + if err != nil { + return err + } + + _, err = stmt.Exec(session.rcpt_addr_id, session.from, bytes) + if err != nil { + return err + } + + err = session.tx.Commit() + if err != nil { + return err + } + + log.Println("New mail from <", session.from, "> to <", session.rcpt, ">") + log.Println("Data:", string(bytes)) + + } + return nil +} + +func (session *Session) Reset() {} + +func (session *Session) Logout() error { + return nil +} + +func main() { + db, err := sql.Open("sqlite3", "./db.db") + if err != nil { + log.Fatal(err) + } + defer db.Close() + + backend := &Backend{ + db: db, + } + + server := smtp.NewServer(backend) + + server.Addr = "localhost:1025" + server.Domain = "localhost" + server.WriteTimeout = 60 * time.Second + server.ReadTimeout = 60 * time.Second + server.MaxMessageBytes = 1024 * 1024 + server.MaxRecipients = 50 + server.AllowInsecureAuth = true + + log.Println("Starting server at", server.Addr) + if err := server.ListenAndServe(); err != nil { + log.Fatal(err) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..83cf4d2 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/GRFreire/nthmail + +go 1.21.6 + +require ( + github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect + github.com/emersion/go-smtp v0.20.2 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d38bd91 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +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= +github.com/emersion/go-smtp v0.20.2 h1:peX42Qnh5Q0q3vrAnRy43R/JwTnnv75AebxbkTL7Ia4= +github.com/emersion/go-smtp v0.20.2/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= diff --git a/migration.sql b/migration.sql new file mode 100644 index 0000000..fae11ad --- /dev/null +++ b/migration.sql @@ -0,0 +1,12 @@ +CREATE TABLE inboxes ( + id integer not null primary key, + addr text unique +); + +CREATE TABLE mails ( + id integer not null primary key, + inbox_id id, + from_addr text, + data blob, + FOREIGN KEY(inbox_id) REFERENCES inboxes(id) +);