Make SessionManager create the IMAP client

This will allow SessionManager to re-connect when the IMAP server logs
the user out.
This commit is contained in:
Simon Ser 2019-12-09 18:35:51 +01:00
parent efd401bfbf
commit 7702925497
No known key found for this signature in database
GPG key ID: 0FDE7BE0E88F5E48
4 changed files with 46 additions and 23 deletions

View file

@ -81,18 +81,11 @@ func handleLogin(ectx echo.Context) error {
username := ctx.FormValue("username") username := ctx.FormValue("username")
password := ctx.FormValue("password") password := ctx.FormValue("password")
if username != "" && password != "" { if username != "" && password != "" {
conn, err := ctx.server.connectIMAP() token, err := ctx.server.sessions.Put(username, password)
if err != nil {
return err
}
if err := conn.Login(username, password); err != nil {
conn.Logout()
return ctx.Render(http.StatusOK, "login.html", nil)
}
token, err := ctx.server.sessions.Put(conn, username, password)
if err != nil { if err != nil {
if _, ok := err.(AuthError); ok {
return ctx.Render(http.StatusOK, "login.html", nil)
}
return fmt.Errorf("failed to put connection in pool: %v", err) return fmt.Errorf("failed to put connection in pool: %v", err)
} }
ctx.setToken(token) ctx.setToken(token)

View file

@ -38,15 +38,15 @@ func (p *luaPlugin) onRender(l *lua.LState) int {
func (p *luaPlugin) setFilter(l *lua.LState) int { func (p *luaPlugin) setFilter(l *lua.LState) int {
name := l.CheckString(1) name := l.CheckString(1)
f := l.CheckFunction(2) f := l.CheckFunction(2)
p.filters[name] = func(args... interface{}) string { p.filters[name] = func(args ...interface{}) string {
luaArgs := make([]lua.LValue, len(args)) luaArgs := make([]lua.LValue, len(args))
for i, v := range args { for i, v := range args {
luaArgs[i] = luar.New(l, v) luaArgs[i] = luar.New(l, v)
} }
err := l.CallByParam(lua.P{ err := l.CallByParam(lua.P{
Fn: f, Fn: f,
NRet: 1, NRet: 1,
Protect: true, Protect: true,
}, luaArgs...) }, luaArgs...)
if err != nil { if err != nil {

View file

@ -76,7 +76,7 @@ func (s *Server) parseSMTPURL(smtpURL string) error {
func newServer(imapURL, smtpURL string) (*Server, error) { func newServer(imapURL, smtpURL string) (*Server, error) {
s := &Server{} s := &Server{}
s.sessions = NewSessionManager() s.sessions = NewSessionManager(s.connectIMAP)
if err := s.parseIMAPURL(imapURL); err != nil { if err := s.parseIMAPURL(imapURL); err != nil {
return nil, err return nil, err

View file

@ -4,6 +4,7 @@ import (
"crypto/rand" "crypto/rand"
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt"
"sync" "sync"
imapclient "github.com/emersion/go-imap/client" imapclient "github.com/emersion/go-imap/client"
@ -20,6 +21,14 @@ func generateToken() (string, error) {
var ErrSessionExpired = errors.New("session expired") var ErrSessionExpired = errors.New("session expired")
type AuthError struct {
cause error
}
func (err AuthError) Error() string {
return fmt.Sprintf("authentication failed: %v", err.cause)
}
type Session struct { type Session struct {
locker sync.Mutex locker sync.Mutex
imapConn *imapclient.Client imapConn *imapclient.Client
@ -35,16 +44,32 @@ func (s *Session) Do(f func(*imapclient.Client) error) error {
// TODO: expiration timer // TODO: expiration timer
type SessionManager struct { type SessionManager struct {
locker sync.Mutex locker sync.Mutex
sessions map[string]*Session sessions map[string]*Session
newIMAPClient func() (*imapclient.Client, error)
} }
func NewSessionManager() *SessionManager { func NewSessionManager(newIMAPClient func() (*imapclient.Client, error)) *SessionManager {
return &SessionManager{ return &SessionManager{
sessions: make(map[string]*Session), sessions: make(map[string]*Session),
newIMAPClient: newIMAPClient,
} }
} }
func (sm *SessionManager) connect(username, password string) (*imapclient.Client, error) {
c, err := sm.newIMAPClient()
if err != nil {
return nil, err
}
if err := c.Login(username, password); err != nil {
c.Logout()
return nil, AuthError{err}
}
return c, nil
}
func (sm *SessionManager) Get(token string) (*Session, error) { func (sm *SessionManager) Get(token string) (*Session, error) {
sm.locker.Lock() sm.locker.Lock()
defer sm.locker.Unlock() defer sm.locker.Unlock()
@ -56,7 +81,12 @@ func (sm *SessionManager) Get(token string) (*Session, error) {
return session, nil return session, nil
} }
func (sm *SessionManager) Put(imapConn *imapclient.Client, username, password string) (token string, err error) { func (sm *SessionManager) Put(username, password string) (token string, err error) {
c, err := sm.connect(username, password)
if err != nil {
return "", err
}
sm.locker.Lock() sm.locker.Lock()
defer sm.locker.Unlock() defer sm.locker.Unlock()
@ -64,7 +94,7 @@ func (sm *SessionManager) Put(imapConn *imapclient.Client, username, password st
var err error var err error
token, err = generateToken() token, err = generateToken()
if err != nil { if err != nil {
imapConn.Logout() c.Logout()
return "", err return "", err
} }
@ -74,13 +104,13 @@ func (sm *SessionManager) Put(imapConn *imapclient.Client, username, password st
} }
sm.sessions[token] = &Session{ sm.sessions[token] = &Session{
imapConn: imapConn, imapConn: c,
username: username, username: username,
password: password, password: password,
} }
go func() { go func() {
<-imapConn.LoggedOut() <-c.LoggedOut()
sm.locker.Lock() sm.locker.Lock()
delete(sm.sessions, token) delete(sm.sessions, token)