Skip to content

Commit

Permalink
Very early NOTIFY support, hopefully fix -password on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
neilalexander committed Jul 10, 2021
1 parent 4c07013 commit 63de5e8
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 31 deletions.
42 changes: 16 additions & 26 deletions cmd/yggmail/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"os"
"strings"
"sync"
"syscall"

"github.com/emersion/go-imap/server"
"github.com/emersion/go-sasl"
"github.com/emersion/go-smtp"
"golang.org/x/term"
Expand Down Expand Up @@ -86,13 +86,13 @@ func main() {
switch {
case password != nil && *password:
log.Println("Please enter your new password:")
password1, err := term.ReadPassword(0)
password1, err := term.ReadPassword(syscall.Stdin)
if err != nil {
panic(err)
}
fmt.Println()
log.Println("Please enter your new password again:")
password2, err := term.ReadPassword(0)
password2, err := term.ReadPassword(syscall.Stdin)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -127,31 +127,19 @@ func main() {
}

queues := smtpsender.NewQueues(cfg, log, transport, storage)
var notify *imapserver.IMAPNotify

go func() {
defer wg.Done()

imapBackend := &imapserver.Backend{
Log: log,
Config: cfg,
Storage: storage,
}

imapServer := server.New(imapBackend)
imapServer.Addr = *imapaddr
imapServer.AllowInsecureAuth = true
imapServer.EnableAuth(sasl.Login, func(conn server.Conn) sasl.Server {
return sasl.NewLoginServer(func(username, password string) error {
_, err := imapBackend.Login(nil, username, password)
return err
})
})
imapBackend := &imapserver.Backend{
Log: log,
Config: cfg,
Storage: storage,
}

log.Println("Listening for IMAP on:", imapServer.Addr)
if err := imapServer.ListenAndServe(); err != nil {
log.Fatal(err)
}
}()
_, notify, err = imapserver.NewIMAPServer(imapBackend, *imapaddr, true)
if err != nil {
log.Fatal(err)
}
log.Println("Listening for IMAP on:", *imapaddr)

go func() {
defer wg.Done()
Expand All @@ -162,6 +150,7 @@ func main() {
Config: cfg,
Storage: storage,
Queues: queues,
Notify: notify,
}

localServer := smtp.NewServer(localBackend)
Expand Down Expand Up @@ -192,6 +181,7 @@ func main() {
Config: cfg,
Storage: storage,
Queues: queues,
Notify: notify,
}

overlayServer := smtp.NewServer(overlayBackend)
Expand Down
21 changes: 21 additions & 0 deletions internal/imapserver/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Backend struct {
Config *config.Config
Log *log.Logger
Storage storage.Storage
Server *IMAPServer
}

func (b *Backend) Login(conn *imap.ConnInfo, username, password string) (backend.User, error) {
Expand All @@ -38,6 +39,26 @@ func (b *Backend) Login(conn *imap.ConnInfo, username, password string) (backend
user := &User{
backend: b,
username: username,
conn: conn,
}
return user, nil
}

/*
func (b *Backend) NotifyNew(id int) error {
b.Server.server.ForEachConn(func(conn server.Conn) {
notify := false
for _, cap := range conn.Capabilities() {
if cap == "NOTIFY" {
notify = true
}
}
if !notify {
return
}
conn.WaitReady()
conn.WriteResp()
})
return nil
}
*/
24 changes: 22 additions & 2 deletions internal/imapserver/imap.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,40 @@
package imapserver

import (
"log"
"os"

idle "github.com/emersion/go-imap-idle"
"github.com/emersion/go-imap/server"
"github.com/emersion/go-sasl"
)

type IMAPServer struct {
server *server.Server
backend *Backend
}

func NewIMAPServer(backend *Backend) (*IMAPServer, error) {
func NewIMAPServer(backend *Backend, addr string, insecure bool) (*IMAPServer, *IMAPNotify, error) {
s := &IMAPServer{
server: server.New(backend),
backend: backend,
}
notify := NewIMAPNotify(s.server, backend.Log)
s.server.Addr = addr
s.server.AllowInsecureAuth = insecure
s.server.Debug = os.Stdout
s.server.Enable(idle.NewExtension())
return s, nil
s.server.Enable(notify)
s.server.EnableAuth(sasl.Login, func(conn server.Conn) sasl.Server {
return sasl.NewLoginServer(func(username, password string) error {
_, err := s.backend.Login(nil, username, password)
return err
})
})
go func() {
if err := s.server.ListenAndServe(); err != nil {
log.Fatal(err)
}
}()
return s, notify, nil
}
65 changes: 65 additions & 0 deletions internal/imapserver/notify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package imapserver

import (
"fmt"
"log"

"github.com/emersion/go-imap"
"github.com/emersion/go-imap/server"
)

type IMAPNotifyHandler struct {
imap.Command
}

func (h *IMAPNotifyHandler) Handle(conn server.Conn) error {
// TODO: Support setting NOTIFY subscriptions or not
return nil
}

type IMAPNotify struct {
server *server.Server
log *log.Logger
}

func (ext *IMAPNotify) Capabilities(c server.Conn) []string {
if c.Context().State&imap.AuthenticatedState != 0 {
return []string{"NOTIFY"}
}
return nil
}

func (ext *IMAPNotify) Command(name string) server.HandlerFactory {
if name != "NOTIFY" {
return nil
}
return func() server.Handler {
return &IMAPNotifyHandler{}
}
}

func (ext *IMAPNotify) NotifyNew(id, count int) error {
ext.server.ForEachConn(func(c server.Conn) {
var resptype imap.StatusRespType
if mailbox := c.Context().Mailbox; mailbox != nil && mailbox.Name() == "INBOX" {
resptype = imap.StatusRespType(
fmt.Sprintf("EXISTS %d", id),
)
} else {
resptype = imap.StatusRespType(
fmt.Sprintf("STATUS INBOX (UIDNEXT %d MESSAGES %d)", id+1, count),
)
}
_ = c.WriteResp(&imap.StatusResp{
Type: resptype,
})
})
return nil
}

func NewIMAPNotify(s *server.Server, log *log.Logger) *IMAPNotify {
return &IMAPNotify{
server: s,
log: log,
}
}
2 changes: 2 additions & 0 deletions internal/imapserver/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"errors"
"fmt"

"github.com/emersion/go-imap"
"github.com/emersion/go-imap/backend"
)

type User struct {
backend *Backend
username string
conn *imap.ConnInfo
}

func (u *User) Username() string {
Expand Down
2 changes: 2 additions & 0 deletions internal/smtpserver/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/emersion/go-smtp"
"github.com/neilalexander/yggmail/internal/config"
"github.com/neilalexander/yggmail/internal/imapserver"
"github.com/neilalexander/yggmail/internal/smtpsender"
"github.com/neilalexander/yggmail/internal/storage"
"github.com/neilalexander/yggmail/internal/utils"
Expand All @@ -25,6 +26,7 @@ type Backend struct {
Config *config.Config
Queues *smtpsender.Queues
Storage storage.Storage
Notify *imapserver.IMAPNotify
}

func (b *Backend) Login(state *smtp.ConnectionState, username, password string) (smtp.Session, error) {
Expand Down
11 changes: 9 additions & 2 deletions internal/smtpserver/session_remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,17 @@ func (s *SessionRemote) Data(r io.Reader) error {
return fmt.Errorf("m.WriteTo: %w", err)
}

if _, err := s.backend.Storage.MailCreate("INBOX", b.Bytes()); err != nil {
if id, err := s.backend.Storage.MailCreate("INBOX", b.Bytes()); err != nil {
return fmt.Errorf("s.backend.Storage.StoreMessageFor: %w", err)
} else {
s.backend.Log.Printf("Stored new mail from %s", s.from)

if count, err := s.backend.Storage.MailCount("INBOX"); err == nil {
if err := s.backend.Notify.NotifyNew(id, count); err != nil {
s.backend.Log.Println("Failed to notify:", s.from)
}
}
}
s.backend.Log.Printf("Stored new mail from %s", s.from)

return nil
}
Expand Down
5 changes: 4 additions & 1 deletion internal/smtpserver/smtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ package smtpserver

import (
"github.com/emersion/go-smtp"
"github.com/neilalexander/yggmail/internal/imapserver"
)

type SMTPServer struct {
server *smtp.Server
backend smtp.Backend
notify *imapserver.IMAPNotify
}

func NewSMTPServer(backend smtp.Backend) *SMTPServer {
func NewSMTPServer(backend smtp.Backend, notify *imapserver.IMAPNotify) *SMTPServer {
s := &SMTPServer{
server: smtp.NewServer(backend),
backend: backend,
notify: notify,
}
return s
}

0 comments on commit 63de5e8

Please sign in to comment.