aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md3
-rw-r--r--common_imap.go31
-rw-r--r--config.go5
-rw-r--r--main.go1
-rw-r--r--serve_imap.go169
5 files changed, 2 insertions, 207 deletions
diff --git a/README.md b/README.md
index 8dcc94a..01525fc 100644
--- a/README.md
+++ b/README.md
@@ -17,9 +17,10 @@ set up correctly. IMO it'd be easier to integrate it into the SMTP daemon.
- ESMTP: [8BITMIME](https://www.rfc-editor.org/rfc/rfc6152.html),
[SIZE](https://www.rfc-editor.org/rfc/rfc1870.html),
[DSN](https://www.rfc-editor.org/rfc/rfc3464.html)
-- Receiving mail, IMAP side, with TLS
- DKIM, DMARC, SPF, [ARC](https://www.rfc-editor.org/rfc/rfc8617.html)
+- Receiving mail and mailbox synchronization via a custom protocol
- Sending mail, outgoing side, with STARTTLS
- Sending mail, incoming side, with TLS
- A UNIX domain socket protocol for integration with mailing list and forge
software (might be called the Lindenii Mail Delivery Protocol)
+- Receiving mail, IMAP side, with TLS (far future)
diff --git a/common_imap.go b/common_imap.go
deleted file mode 100644
index 3d79124..0000000
--- a/common_imap.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package main
-
-import (
- "errors"
- "strings"
-)
-
-var err_imap_generic = errors.New("Error in IMAP command received by server.")
-
-func parse_imap_line(line string) (tag, cmd, param string, err error) {
- line = strings.TrimSuffix(line, "\n")
- line = strings.TrimSuffix(line, "\r")
- tag_end := strings.IndexByte(line, ' ')
- if tag_end == -1 {
- err = err_imap_generic
- return
- }
- tag = line[:tag_end]
- rest := line[tag_end+1:]
- cmd_end := strings.IndexByte(rest, ' ')
- var param_start int
- if cmd_end == -1 {
- cmd_end = len(rest)
- param_start = len(rest)
- } else {
- param_start = cmd_end + 1
- }
- cmd = strings.ToUpper(rest[:cmd_end])
- param = rest[param_start:]
- return
-}
diff --git a/config.go b/config.go
index b805dbc..e7d6aa4 100644
--- a/config.go
+++ b/config.go
@@ -25,11 +25,6 @@ var config struct {
Net string `scfg:"net"`
Addr string `scfg:"addr"`
} `scfg:"mx"`
- IMAP struct {
- Net string `scfg:"net"`
- Addr string `scfg:"addr"`
- Trans string `scfg:"trans"`
- } `scfg:"imap"`
_tls_config *tls.Config
}
var (
diff --git a/main.go b/main.go
index 60082da..51540f9 100644
--- a/main.go
+++ b/main.go
@@ -18,7 +18,6 @@ func main() {
}
go serve_mx()
- go serve_imap()
deadlock := make(chan struct{})
deadlock <- struct{}{}
diff --git a/serve_imap.go b/serve_imap.go
deleted file mode 100644
index ad50c27..0000000
--- a/serve_imap.go
+++ /dev/null
@@ -1,169 +0,0 @@
-package main
-
-import (
- "bufio"
- "bytes"
- "context"
- "crypto/tls"
- "encoding/base64"
- "fmt"
- "io"
- "net"
- "strings"
-
- "github.com/jackc/pgx/v5/pgxpool"
- "go.lindenii.runxiyu.org/lindenii-common/clog"
-)
-
-type imap_server_state_t uint
-
-const (
- imap_server_state_not_authenticated imap_server_state_t = iota
- imap_server_state_authenticated
- imap_server_state_selected
- imap_server_state_logout
-)
-
-type imap_recv_session struct {
- buf_conn *bufio.ReadWriter
- net_conn net.Conn
- tls_conn *tls.Conn
- my_server_name string
- db *pgxpool.Pool
- server_state imap_server_state_t
-}
-
-const IMAP_CAPABILITIES = "CAPABILITY IMAP4rev2 AUTH=PLAIN"
-
-func (session *imap_recv_session) handle(ctx context.Context) error {
- session.server_state = imap_server_state_not_authenticated
- session.buf_conn = bufio.NewReadWriter(bufio.NewReader(session.net_conn), bufio.NewWriter(session.net_conn))
- _, _ = session.buf_conn.WriteString("* OK [" + IMAP_CAPABILITIES + "] " + VERSION + "\r\n")
- _ = session.buf_conn.Flush()
-
- for {
- line, err := session.buf_conn.ReadString('\n')
- if err != nil {
- if err == io.EOF {
- return err_connection_handler_eof
- }
- return err
- }
- tag, cmd, param, err := parse_imap_line(line)
- if err != nil {
- _, _ = session.buf_conn.WriteString(line + " BAD " + err.Error() + "\r\n")
- _ = session.buf_conn.Flush()
- continue
- }
- clog.Debug(fmt.Sprintf("tag=%#v, cmd=%#v, param=%#v", tag, cmd, param))
- switch_cmd:
- switch cmd {
- case "CAPABILITY":
- _, _ = session.buf_conn.WriteString("* CAPABILITY " + IMAP_CAPABILITIES + "\r\n")
- _, _ = session.buf_conn.WriteString(tag + " OK CAPABILITY completed\r\n")
- _ = session.buf_conn.Flush()
- case "AUTHENTICATE":
- if session.server_state != imap_server_state_authenticated {
- _, _ = session.buf_conn.WriteString(line + " TODO you are already authenticated")
- _ = session.buf_conn.Flush()
- break switch_cmd
- }
- space_that_ends_the_method := strings.IndexByte(param, ' ')
- method := param[:space_that_ends_the_method]
- switch method {
- case "PLAIN":
- argument_base64 := param[space_that_ends_the_method+1:]
- if strings.IndexByte(argument_base64, ' ') != -1 {
- _, _ = session.buf_conn.WriteString(tag + " TODO too many parameters\r\n")
- _ = session.buf_conn.Flush()
- break switch_cmd
- }
- argument := make([]byte, base64.StdEncoding.DecodedLen(len(argument_base64)))
- _, err := base64.StdEncoding.Decode(argument, []byte(argument_base64))
- if err != nil {
- _, _ = session.buf_conn.WriteString(tag + " TODO cannot decode base64\r\n")
- _ = session.buf_conn.Flush()
- break switch_cmd
- }
- segments := bytes.Split(argument, []byte{0})
- if len(segments) != 3 {
- session.buf_conn.WriteString(" TODO need three sasl segments\r\n")
- break switch_cmd
- }
- authcid, passwd := string(segments[1]), string(segments[2])
- if authcid == "test" && passwd == "test" { // TODO
- session.server_state = imap_server_state_authenticated
- } else {
-
- }
- default:
- _, _ = session.buf_conn.WriteString(line + " TODO i don't know this authentication method\r\n")
- _ = session.buf_conn.Flush()
- break switch_cmd
- }
- }
- }
-}
-
-func imap_new_session(ctx context.Context, net_conn net.Conn) error {
- session := imap_recv_session{
- net_conn: net_conn,
- }
- return session.handle(ctx)
-}
-
-func serve_imap() {
- var imap_net, imap_addr, imap_trans string
- var tls_config *tls.Config
- config_consistent_run(func() {
- imap_net = config.IMAP.Net
- imap_addr = config.IMAP.Addr
- imap_trans = config.IMAP.Trans
- tls_config = config._tls_config
- })
- var listener net.Listener
- var err error
- switch imap_trans {
- case "tls", "":
- listener, err = tls.Listen(imap_net, imap_addr, tls_config)
- if err != nil {
- clog.Fatal(1, "IMAP: Cannot listen TLS: "+err.Error())
- }
- case "plain":
- listener, err = net.Listen(imap_net, imap_addr)
- if err != nil {
- clog.Fatal(1, "IMAP: Cannot listen plain: "+err.Error())
- }
- default:
- clog.Fatal(1, fmt.Sprintf("IMAP: Listen transport %#v is invalid", imap_trans))
- }
-
- if err != nil {
- clog.Fatal(1, "IMAP: Cannot listen: "+err.Error())
- }
- defer listener.Close()
- clog.Info("IMAP: Listening via " + imap_net + " on " + imap_addr)
-
- for {
- conn, err := listener.Accept()
- if err != nil {
- clog.Error("IMAP: Cannot accept connection: " + err.Error())
- }
- clog.Info("IMAP: Accepted connection from " + conn.RemoteAddr().String())
-
- go func() {
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
- err := imap_new_session(ctx, conn)
- if err != nil {
- if err == err_connection_handler_eof {
- clog.Info("IMAP: Connection for " + conn.RemoteAddr().String() + " closed with EOF")
- } else {
- clog.Error("IMAP: Connection handler for " + conn.RemoteAddr().String() + " returned error: " + err.Error())
- }
- } else {
- clog.Info("IMAP: Connection for " + conn.RemoteAddr().String() + " closed gracefully")
- }
- }()
- }
-}