Skip to content

Commit

Permalink
Merge pull request #32 from arnisoph/catch_connection_closed
Browse files Browse the repository at this point in the history
Catch IMAP connection disconnect by server
  • Loading branch information
arnisoph authored Mar 27, 2020
2 parents 878b0c7 + f7ae225 commit 5625bf2
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 46 deletions.
24 changes: 23 additions & 1 deletion cmd/postisto/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,35 @@ func runApp(configPath string, logLevel string, logJSON bool, pollInterval time.
}

acc := cfg.Accounts[name]

if err := acc.Connection.Connect(); err != nil {
return fmt.Errorf("failed to initially connect to server %q with username %q", acc.Connection.Server, acc.Connection.Username)
}

accs = append(accs, &accInfo{name: name, acc: &acc, filters: filters})
}

log.Info("Entering continuously running mail search & filter loop. Waiting for mails...")
if onetime {
log.Info("Entering mail search & filter loop once and exit then immediately")
} else {
log.Info("Entering continuously running mail search & filter loop. Waiting for mails...")
}

for {
for _, accInfo := range accs {
if err := filter.EvaluateFilterSetsOnMsgs(&accInfo.acc.Connection, *accInfo.acc.InputMailbox, []string{server.SeenFlag, server.FlaggedFlag}, *accInfo.acc.FallbackMailbox, accInfo.filters); err != nil {
if server.IsDisconnected(err) {
// this can happen, so let's just reconnect
//TODO implement exponential backoff to avoid too much noise?

time.Sleep(time.Second * 3)
if err := accInfo.acc.Connection.Connect(); err != nil {
return fmt.Errorf("failed to reconnect to server %q with username %q", accInfo.acc.Connection.Server, accInfo.acc.Connection.Username)
}

continue
}

return fmt.Errorf("failed to run filter engine: %v", err)
}
}
Expand Down
90 changes: 45 additions & 45 deletions pkg/server/imap.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package server

import (
"bytes"
"errors"
"fmt"
"github.com/arnisoph/postisto/pkg/log"
imapUtil "github.com/emersion/go-imap"
Expand All @@ -21,6 +22,27 @@ const (
RecentFlag = "\\Recent"
)

var ErrConnectionClosed = errors.New("imap: connection closed")

func IsDisconnected(err error) bool { //TODO https://github.com/emersion/go-imap/issues/348
return err.Error() == ErrConnectionClosed.Error()
}

func (conn *Connection) requiresReconnect() bool {
return conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState)
}

func (conn *Connection) ensureConnected() error {
if conn.requiresReconnect() {
log.Infow("Server connection lost, trying to reconnect...", "server", conn.Server, "username", conn.Username)
if err := conn.Connect(); err != nil {
return err
}
}

return nil
}

func (conn *Connection) Upload(file string, mailbox string, flags []string) error {
data, err := os.Open(file)
defer data.Close()
Expand All @@ -37,12 +59,9 @@ func (conn *Connection) Upload(file string, mailbox string, flags []string) erro
return err
}

//if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return err
}
if err := conn.ensureConnected(); err != nil {
return err
}

// Select mailbox
Expand All @@ -62,10 +81,8 @@ func (conn *Connection) Upload(file string, mailbox string, flags []string) erro

func (conn *Connection) Search(mailbox string, withFlags []string, withoutFlags []string) ([]uint32, error) {
// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return nil, err
}
if err := conn.ensureConnected(); err != nil {
return nil, err
}

// Select mailbox
Expand All @@ -89,10 +106,8 @@ func (conn *Connection) Search(mailbox string, withFlags []string, withoutFlags

func (conn *Connection) Fetch(mailbox string, uids []uint32) ([]*Message, error) {
// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return nil, err
}
if err := conn.ensureConnected(); err != nil {
return nil, err
}

// Select mailbox
Expand Down Expand Up @@ -137,10 +152,8 @@ func (conn *Connection) Fetch(mailbox string, uids []uint32) ([]*Message, error)

func (conn *Connection) SearchAndFetch(mailbox string, withFlags []string, withoutFlags []string) ([]*Message, error) {
// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return nil, err
}
if err := conn.ensureConnected(); err != nil {
return nil, err
}

uids, err := conn.Search(mailbox, withFlags, withoutFlags)
Expand All @@ -158,10 +171,8 @@ func (conn *Connection) DeleteMsgs(mailbox string, uids []uint32, expunge bool)

func (conn *Connection) SetFlags(mailbox string, uids []uint32, flagOp string, flags []interface{}, expunge bool) error {
// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return err
}
if err := conn.ensureConnected(); err != nil {
return err
}

log.Debugw("Starting to set flags on mails", "mailbox", mailbox, "uids", uids, "flagOp", flagOp, "flags", flags, "expunge", expunge)
Expand Down Expand Up @@ -201,10 +212,8 @@ func (conn *Connection) GetFlags(mailbox string, uid uint32) ([]string, error) {
log.Debugw("Starting to get flags from a mail", "mailbox", mailbox, "uid", uid)

// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return nil, err
}
if err := conn.ensureConnected(); err != nil {
return nil, err
}

// Select mailbox
Expand Down Expand Up @@ -240,10 +249,8 @@ func (conn *Connection) CreateMailbox(name string) error {
log.Infow("Creating new mailbox", "mailbox", name)

// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return err
}
if err := conn.ensureConnected(); err != nil {
return err
}

if err := conn.imapClient.Create(name); err != nil {
Expand All @@ -258,10 +265,8 @@ func (conn *Connection) DeleteMailbox(name string) error {
log.Infow("Deleting mailbox", "mailbox", name)

// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return err
}
if err := conn.ensureConnected(); err != nil {
return err
}

if err := conn.imapClient.Delete(name); err != nil {
Expand All @@ -275,10 +280,8 @@ func (conn *Connection) DeleteMailbox(name string) error {
// List mailboxes
func (conn *Connection) List() (map[string]imapUtil.MailboxInfo, error) {
// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return nil, err
}
if err := conn.ensureConnected(); err != nil {
return nil, err
}

mailboxesChan := make(chan *imapUtil.MailboxInfo)
Expand Down Expand Up @@ -339,10 +342,8 @@ func (conn *Connection) List() (map[string]imapUtil.MailboxInfo, error) {

func (conn *Connection) Move(uids []uint32, from string, to string) error {
// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return err
}
if err := conn.ensureConnected(); err != nil {
return err
}

var err error
Expand Down Expand Up @@ -392,12 +393,11 @@ func (conn *Connection) Move(uids []uint32, from string, to string) error {

func (conn *Connection) Select(mailbox string, readOnly bool, autoCreate bool) (*imapUtil.MailboxStatus, error) {
// Re-login if necessary
if conn.imapClient == nil || (conn.imapClient.State() != imapUtil.AuthenticatedState && conn.imapClient.State() != imapUtil.SelectedState) {
if err := conn.Connect(); err != nil {
return nil, err
}
if err := conn.ensureConnected(); err != nil {
return nil, err
}

log.Debugw("Selecting mailbox", "mailbox", mailbox)
status, err := conn.imapClient.Select(mailbox, readOnly)

if err == nil {
Expand Down

0 comments on commit 5625bf2

Please sign in to comment.