mirror of
https://github.com/pulb/mailnag.git
synced 2026-05-07 01:07:46 +02:00
IMAP code moved from idlers.py to backends/imap.py.
Folder is always selected when new connection is opened, not only for idle accounts. Callback is set using Account.notify_next_change method. Connection abort checking in callback is moved to IMAP backend.
This commit is contained in:
@@ -27,6 +27,7 @@ import email
|
||||
import logging
|
||||
import re
|
||||
import Mailnag.common.imaplib2 as imaplib
|
||||
from Mailnag.common.imaplib2 import AUTH
|
||||
|
||||
class ImapBackend:
|
||||
"""Implementation of IMAP mail boxes."""
|
||||
@@ -42,6 +43,7 @@ class ImapBackend:
|
||||
self.ssl = ssl # bool
|
||||
self.folders = folders
|
||||
self._conn = None
|
||||
self._conn_closed = True
|
||||
|
||||
|
||||
def get_connection(self, use_existing):
|
||||
@@ -82,18 +84,29 @@ class ImapBackend:
|
||||
except: pass
|
||||
raise # re-throw exception
|
||||
|
||||
self._conn_closed = False
|
||||
|
||||
# Need to get out of AUTH mode of fresh connections.
|
||||
if self._conn.state == AUTH:
|
||||
self.select()
|
||||
|
||||
return self._conn
|
||||
|
||||
|
||||
def close(self):
|
||||
self._conn.close()
|
||||
# if conn has already been closed, don't try to close it again
|
||||
if not self._conn_closed:
|
||||
self._conn.close()
|
||||
self._conn_closed = True
|
||||
self._conn.logout()
|
||||
self._conn = None
|
||||
|
||||
|
||||
def has_connection(self):
|
||||
return (self._conn != None) and \
|
||||
(self._conn.state != imaplib.LOGOUT) and \
|
||||
(not self._conn.Terminate)
|
||||
(not self._conn.Terminate) and \
|
||||
(not self._conn_closed)
|
||||
|
||||
|
||||
def list_messages(self):
|
||||
@@ -151,3 +164,38 @@ class ImapBackend:
|
||||
|
||||
return lst
|
||||
|
||||
|
||||
# TODO: Temporarily public. Make private.
|
||||
def select(self):
|
||||
if len(self.folders) == 1:
|
||||
self._conn.select(self.folders[0])
|
||||
else:
|
||||
self._conn.select("INBOX")
|
||||
|
||||
|
||||
def notify_next_change(self, callback=None, timeout=None):
|
||||
# register idle callback that is called whenever an idle event
|
||||
# arrives (new mail / mail deleted).
|
||||
# the callback is called after <idle_timeout> minutes at the latest.
|
||||
# gmail sends keepalive events every 5 minutes.
|
||||
|
||||
# idle callback (runs on a further thread)
|
||||
def _idle_callback(args):
|
||||
# check if the connection has been reset by provider
|
||||
self._conn_closed = (args[2] != None) and (args[2][0] is self._conn.abort)
|
||||
|
||||
# call actual callback
|
||||
callback(args)
|
||||
|
||||
self._conn.idle(callback = _idle_callback, timeout = timeout)
|
||||
|
||||
|
||||
def cancel_notifications(self):
|
||||
try:
|
||||
if self._conn != None:
|
||||
# Exit possible active idle state.
|
||||
# (also calls idle_callback)
|
||||
self._conn.noop()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@@ -92,6 +92,19 @@ class Account:
|
||||
return self.backend.list_messages()
|
||||
|
||||
|
||||
# TODO: Temporarily here. Remove when no needed.
|
||||
def select(self):
|
||||
self.backend.select()
|
||||
|
||||
|
||||
def notify_next_change(self, callback=None, timeout=None):
|
||||
self.backend.notify_next_change(callback, timeout)
|
||||
|
||||
|
||||
def cancel_notifications(self):
|
||||
self.backend.cancel_notifications()
|
||||
|
||||
|
||||
# Requests folder names (list) from a server.
|
||||
# Relevant for IMAP accounts only.
|
||||
# Returns an empty list when used on POP3 accounts.
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
import threading
|
||||
import time
|
||||
import logging
|
||||
from Mailnag.common.imaplib2 import AUTH
|
||||
from Mailnag.common.exceptions import InvalidOperationException
|
||||
|
||||
|
||||
@@ -41,14 +40,7 @@ class Idler(object):
|
||||
self._sync_callback = sync_callback
|
||||
self._account = account
|
||||
self._idle_timeout = idle_timeout
|
||||
# use_existing = True:
|
||||
# connection has been opened in mailnagdaemon.py already (immediate check)
|
||||
self._conn = account.get_connection(use_existing = True)
|
||||
self._disposed = False
|
||||
|
||||
# Need to get out of AUTH mode of fresh connections.
|
||||
if self._conn.state == AUTH:
|
||||
self._select(self._conn, account)
|
||||
|
||||
|
||||
def start(self):
|
||||
@@ -63,34 +55,27 @@ class Idler(object):
|
||||
self._event.set()
|
||||
self._thread.join()
|
||||
|
||||
try:
|
||||
if self._conn != None:
|
||||
# Exit possible active idle state.
|
||||
# (also calls idle_callback)
|
||||
self._conn.noop()
|
||||
except:
|
||||
pass
|
||||
|
||||
self._disposed = True
|
||||
logging.info('Idler closed')
|
||||
|
||||
|
||||
# idle thread
|
||||
def _idle(self):
|
||||
# use_existing = True:
|
||||
# connection has been opened in mailnagdaemon.py already (immediate check)
|
||||
self._conn = self._account.get_connection(use_existing = True)
|
||||
|
||||
while True:
|
||||
# if the event is set here,
|
||||
# disposed() must have been called
|
||||
# so stop the idle thread.
|
||||
if self._event.isSet():
|
||||
return
|
||||
break
|
||||
|
||||
self._needsync = False
|
||||
self._conn_closed = False
|
||||
|
||||
# register idle callback that is called whenever an idle event arrives (new mail / mail deleted).
|
||||
# the callback is called after <idle_timeout> minutes at the latest.
|
||||
# gmail sends keepalive events every 5 minutes.
|
||||
self._conn.idle(callback = self._idle_callback, timeout = 60 * self._idle_timeout)
|
||||
self._account.notify_next_change(callback = self._idle_callback, timeout = 60 * self._idle_timeout)
|
||||
|
||||
# waits for the event to be set
|
||||
# (in idle callback or in dispose())
|
||||
@@ -99,17 +84,17 @@ class Idler(object):
|
||||
# if the event is set due to idle sync
|
||||
if self._needsync:
|
||||
self._event.clear()
|
||||
if self._conn_closed:
|
||||
if not self._account.has_connection():
|
||||
self._reconnect()
|
||||
|
||||
if self._conn != None:
|
||||
if self._account.has_connection():
|
||||
self._sync_callback(self._account)
|
||||
|
||||
self._account.cancel_notifications()
|
||||
|
||||
|
||||
# idle callback (runs on a further thread)
|
||||
def _idle_callback(self, args):
|
||||
# check if the connection has been reset by provider
|
||||
self._conn_closed = (args[2] != None) and (args[2][0] is self._conn.abort)
|
||||
# flag that a mail sync is needed
|
||||
self._needsync = True
|
||||
# trigger waiting _idle thread
|
||||
@@ -120,14 +105,10 @@ class Idler(object):
|
||||
# connection has been reset by provider -> try to reconnect
|
||||
logging.info("Idler thread for account '%s' has been disconnected" % self._account.name)
|
||||
|
||||
# conn has already been closed, don't try to close it again
|
||||
# self._conn.close() # (calls idle_callback)
|
||||
|
||||
# shutdown existing callback thread
|
||||
self._conn.logout()
|
||||
self._account.close()
|
||||
self._conn = None
|
||||
|
||||
while (self._conn == None) and (not self._event.isSet()):
|
||||
while (not self._account.has_connection()) and (not self._event.isSet()):
|
||||
logging.info("Trying to reconnect Idler thread for account '%s'." % self._account.name)
|
||||
try:
|
||||
self._conn = self._account.get_connection(use_existing = False)
|
||||
@@ -137,18 +118,8 @@ class Idler(object):
|
||||
logging.info("Trying to reconnect Idler thread for account '%s' in %s minutes" %
|
||||
(self._account.name, str(self.RECONNECT_RETRY_INTERVAL)))
|
||||
self._wait(60 * self.RECONNECT_RETRY_INTERVAL) # don't hammer the server
|
||||
|
||||
if self._conn != None:
|
||||
self._select(self._conn, self._account)
|
||||
|
||||
|
||||
def _select(self, conn, account):
|
||||
if len(account.folders) == 1:
|
||||
conn.select(account.folders[0])
|
||||
else:
|
||||
conn.select("INBOX")
|
||||
|
||||
|
||||
def _wait(self, secs):
|
||||
start_time = time.time()
|
||||
while (((time.time() - start_time) < secs) and (not self._event.isSet())):
|
||||
|
||||
Reference in New Issue
Block a user