def _start(self): try: # Call Accounts-Loaded plugin hooks for f in self._hookreg.get_hook_funcs(HookTypes.ACCOUNTS_LOADED): try_call(lambda: f(self._accounts)) if not self._wait_for_inet_connection(): return # Immediate check, check *all* accounts try: self._mailchecker.check(self._accounts) except: logging.exception('Caught an exception.') idle_accounts = filter(lambda acc: acc.imap and acc.idle, self._accounts) non_idle_accounts = filter( lambda acc: (not acc.imap) or (acc.imap and not acc.idle), self._accounts) # start polling thread for POP3 accounts and # IMAP accounts without idle support if len(non_idle_accounts) > 0: poll_interval = int(self._cfg.get('core', 'poll_interval')) def poll_func(): try: while True: self._poll_thread_stop.wait(timeout=60.0 * poll_interval) if self._poll_thread_stop.is_set(): break self._mailchecker.check(non_idle_accounts) except: logging.exception('Caught an exception.') self._poll_thread = threading.Thread(target=poll_func) self._poll_thread.start() # start idler threads for IMAP accounts with idle support if len(idle_accounts) > 0: def sync_func(account): try: self._mailchecker.check([account]) except: logging.exception('Caught an exception.') idle_timeout = int(self._cfg.get('core', 'imap_idle_timeout')) self._idlrunner = IdlerRunner(idle_accounts, sync_func, idle_timeout) self._idlrunner.start() except Exception as ex: logging.exception('Caught an exception.') if self._fatal_error_handler != None: self._fatal_error_handler(ex)
def _start(self): try: # Call Accounts-Loaded plugin hooks for f in self._hookreg.get_hook_funcs(HookTypes.ACCOUNTS_LOADED): try_call( lambda: f(self._accounts) ) if not self._wait_for_inet_connection(): return # Immediate check, check *all* accounts try: self._mailchecker.check(self._accounts) except: logging.exception('Caught an exception.') idle_accounts = filter(lambda acc: acc.imap and acc.idle, self._accounts) non_idle_accounts = filter(lambda acc: (not acc.imap) or (acc.imap and not acc.idle), self._accounts) # start polling thread for POP3 accounts and # IMAP accounts without idle support if len(non_idle_accounts) > 0: poll_interval = int(self._cfg.get('core', 'poll_interval')) def poll_func(): try: while True: self._poll_thread_stop.wait(timeout = 60.0 * poll_interval) if self._poll_thread_stop.is_set(): break self._mailchecker.check(non_idle_accounts) except: logging.exception('Caught an exception.') self._poll_thread = threading.Thread(target = poll_func) self._poll_thread.start() # start idler threads for IMAP accounts with idle support if len(idle_accounts) > 0: def sync_func(account): try: self._mailchecker.check([account]) except: logging.exception('Caught an exception.') idle_timeout = int(self._cfg.get('core', 'imap_idle_timeout')) self._idlrunner = IdlerRunner(idle_accounts, sync_func, idle_timeout) self._idlrunner.start() except Exception as ex: logging.exception('Caught an exception.') if self._fatal_error_handler != None: self._fatal_error_handler(ex)
class MailnagDaemon: def __init__(self, fatal_error_handler=None, shutdown_request_handler=None): self._cfg = None self._fatal_error_handler = fatal_error_handler self._shutdown_request_handler = shutdown_request_handler self._plugins = [] self._hookreg = None self._conntest = None self._accounts = None self._mailchecker = None self._start_thread = None self._poll_thread = None self._poll_thread_stop = threading.Event() self._idlrunner = None # Lock ensures that init() and dispose() # are non-reentrant. self._lock = threading.Lock() # Flag indicating complete # daemon initialization. self._initialized = False self._disposed = False # Initializes the daemon and starts checking threads. def init(self): with self._lock: if self._disposed: raise InvalidOperationException("Daemon has been disposed") if self._initialized: raise InvalidOperationException( "Daemon has already been initialized") self._cfg = read_cfg() accountman = AccountManager( CredentialStore.from_string( self._cfg.get('core', 'credentialstore'))) accountman.load_from_cfg(self._cfg, enabled_only=True) self._accounts = accountman.to_list() self._hookreg = HookRegistry() self._conntest = ConnectivityTest(testmode_mapping[self._cfg.get( 'core', 'connectivity_test')]) memorizer = Memorizer() memorizer.load() self._mailchecker = MailChecker(self._cfg, memorizer, self._hookreg, self._conntest) # Note: all code following _load_plugins() should be executed # asynchronously because the dbus plugin requires an active mainloop # (usually started in the programs main function). self._load_plugins(self._cfg, self._hookreg, memorizer) # Start checking for mails asynchronously. self._start_thread = threading.Thread(target=self._start) self._start_thread.start() self._initialized = True def dispose(self): with self._lock: if self._disposed: return # Note: _disposed must be set # before cleaning up resources # (in case an exception occurs) # and before unloading plugins. # Also required by _wait_for_inet_connection(). self._disposed = True # clean up resources if (self._start_thread != None) and (self._start_thread.is_alive()): self._start_thread.join() logging.info('Starter thread exited successfully.') if (self._poll_thread != None) and (self._poll_thread.is_alive()): self._poll_thread_stop.set() self._poll_thread.join() logging.info('Polling thread exited successfully.') if self._idlrunner != None: self._idlrunner.dispose() if self._accounts != None: for acc in self._accounts: if acc.is_open(): acc.close() self._unload_plugins() def is_initialized(self): return self._initialized def is_disposed(self): return self._disposed # Enforces manual mail checks def check_for_mails(self): # Don't allow mail checks before initialization or # after object disposal. F.i. plugins may not be # loaded/unloaded completely or connections may # have been closed already. self._ensure_valid_state() non_idle_accounts = self._get_non_idle_accounts(self._accounts) self._mailchecker.check(non_idle_accounts) def _ensure_valid_state(self): if not self._initialized: raise InvalidOperationException("Daemon has not been initialized") if self._disposed: raise InvalidOperationException("Daemon has been disposed") def _start(self): try: # Call Accounts-Loaded plugin hooks for f in self._hookreg.get_hook_funcs(HookTypes.ACCOUNTS_LOADED): try_call(lambda: f(self._accounts)) if not self._wait_for_inet_connection(): return # Immediate check, check *all* accounts try: self._mailchecker.check(self._accounts) except: logging.exception('Caught an exception.') idle_accounts = self._get_idle_accounts(self._accounts) non_idle_accounts = self._get_non_idle_accounts(self._accounts) # start polling thread for POP3 accounts and # IMAP accounts without idle support if len(non_idle_accounts) > 0: poll_interval = int(self._cfg.get('core', 'poll_interval')) def poll_func(): try: while True: self._poll_thread_stop.wait(timeout=60.0 * poll_interval) if self._poll_thread_stop.is_set(): break self._mailchecker.check(non_idle_accounts) except: logging.exception('Caught an exception.') self._poll_thread = threading.Thread(target=poll_func) self._poll_thread.start() # start idler threads for IMAP accounts with idle support if len(idle_accounts) > 0: def sync_func(account): try: self._mailchecker.check([account]) except: logging.exception('Caught an exception.') idle_timeout = int(self._cfg.get('core', 'imap_idle_timeout')) self._idlrunner = IdlerRunner(idle_accounts, sync_func, idle_timeout) self._idlrunner.start() except Exception as ex: logging.exception('Caught an exception.') if self._fatal_error_handler != None: self._fatal_error_handler(ex) def _wait_for_inet_connection(self): if self._conntest.is_offline(): logging.info('Waiting for internet connection...') while True: if self._disposed: return False if not self._conntest.is_offline(): return True # Note: don't sleep too long # (see timeout in mailnag.cleanup()) # ..but also don't sleep to short in case of a ping connection test. time.sleep(3) def _get_idle_accounts(self, accounts): return [acc for acc in self._accounts if acc.supports_notifications()] def _get_non_idle_accounts(self, accounts): return [ acc for acc in self._accounts if not acc.supports_notifications() ] def _load_plugins(self, cfg, hookreg, memorizer): class MailnagController_Impl(MailnagController): def __init__(self, daemon, memorizer, hookreg, shutdown_request_hdlr): self._daemon = daemon self._memorizer = memorizer self._hookreg = hookreg self._shutdown_request_handler = shutdown_request_hdlr def get_hooks(self): return self._hookreg def shutdown(self): if self._shutdown_request_handler != None: self._shutdown_request_handler() def check_for_mails(self): self._daemon.check_for_mails() def mark_mail_as_read(self, mail_id): # Note: ensure_valid_state() is not really necessary here # (the memorizer object is available in init() and dispose()), # but better be consistent with other daemon methods. self._daemon._ensure_valid_state() self._memorizer.set_to_seen(mail_id) self._memorizer.save() controller = MailnagController_Impl(self, memorizer, hookreg, self._shutdown_request_handler) enabled_lst = cfg.get('core', 'enabled_plugins').split(',') enabled_lst = filter(lambda s: s != '', map(lambda s: s.strip(), enabled_lst)) self._plugins = Plugin.load_plugins(cfg, controller, enabled_lst) for p in self._plugins: try: p.enable() logging.info("Successfully enabled plugin '%s'." % p.get_modname()) except: logging.error("Failed to enable plugin '%s'." % p.get_modname()) def _unload_plugins(self): if len(self._plugins) > 0: err = False for p in self._plugins: try: p.disable() except: err = True logging.error("Failed to disable plugin '%s'." % p.get_modname()) if not err: logging.info('Plugins disabled successfully.')
class MailnagDaemon: def __init__(self, fatal_error_handler = None, shutdown_request_handler = None): self._cfg = None self._fatal_error_handler = fatal_error_handler self._shutdown_request_handler = shutdown_request_handler self._plugins = [] self._hookreg = None self._conntest = None self._accounts = None self._mailchecker = None self._start_thread = None self._poll_thread = None self._poll_thread_stop = threading.Event() self._idlrunner = None # Lock ensures that init() and dispose() # are non-reentrant. self._lock = threading.Lock() # Flag indicating complete # daemon initialization. self._initialized = False self._disposed = False # Initializes the daemon and starts checking threads. def init(self): with self._lock: if self._disposed: raise InvalidOperationException("Daemon has been disposed") if self._initialized: raise InvalidOperationException("Daemon has already been initialized") self._cfg = read_cfg() accountman = AccountManager(CredentialStore.from_string(self._cfg.get('core', 'credentialstore'))) accountman.load_from_cfg(self._cfg, enabled_only = True) self._accounts = accountman.to_list() self._hookreg = HookRegistry() self._conntest = ConnectivityTest(testmode_mapping[self._cfg.get('core', 'connectivity_test')]) memorizer = Memorizer() memorizer.load() self._mailchecker = MailChecker(self._cfg, memorizer, self._hookreg, self._conntest) # Note: all code following _load_plugins() should be executed # asynchronously because the dbus plugin requires an active mainloop # (usually started in the programs main function). self._load_plugins(self._cfg, self._hookreg, memorizer) # Start checking for mails asynchronously. self._start_thread = threading.Thread(target = self._start) self._start_thread.start() self._initialized = True def dispose(self): with self._lock: if self._disposed: return # Note: _disposed must be set # before cleaning up resources # (in case an exception occurs) # and before unloading plugins. # Also required by _wait_for_inet_connection(). self._disposed = True # clean up resources if (self._start_thread != None) and (self._start_thread.is_alive()): self._start_thread.join() logging.info('Starter thread exited successfully.') if (self._poll_thread != None) and (self._poll_thread.is_alive()): self._poll_thread_stop.set() self._poll_thread.join() logging.info('Polling thread exited successfully.') if self._idlrunner != None: self._idlrunner.dispose() if self._accounts != None: for acc in self._accounts: if acc.is_open(): acc.close() self._unload_plugins() def is_initialized(self): return self._initialized def is_disposed(self): return self._disposed # Enforces manual mail checks def check_for_mails(self): # Don't allow mail checks before initialization or # after object disposal. F.i. plugins may not be # loaded/unloaded completely or connections may # have been closed already. self._ensure_valid_state() non_idle_accounts = filter(lambda acc: (not acc.imap) or (acc.imap and not acc.idle), self._accounts) self._mailchecker.check(non_idle_accounts) def _ensure_valid_state(self): if not self._initialized: raise InvalidOperationException( "Daemon has not been initialized") if self._disposed: raise InvalidOperationException( "Daemon has been disposed") def _start(self): try: # Call Accounts-Loaded plugin hooks for f in self._hookreg.get_hook_funcs(HookTypes.ACCOUNTS_LOADED): try_call( lambda: f(self._accounts) ) if not self._wait_for_inet_connection(): return # Immediate check, check *all* accounts try: self._mailchecker.check(self._accounts) except: logging.exception('Caught an exception.') idle_accounts = filter(lambda acc: acc.imap and acc.idle, self._accounts) non_idle_accounts = filter(lambda acc: (not acc.imap) or (acc.imap and not acc.idle), self._accounts) # start polling thread for POP3 accounts and # IMAP accounts without idle support if len(non_idle_accounts) > 0: poll_interval = int(self._cfg.get('core', 'poll_interval')) def poll_func(): try: while True: self._poll_thread_stop.wait(timeout = 60.0 * poll_interval) if self._poll_thread_stop.is_set(): break self._mailchecker.check(non_idle_accounts) except: logging.exception('Caught an exception.') self._poll_thread = threading.Thread(target = poll_func) self._poll_thread.start() # start idler threads for IMAP accounts with idle support if len(idle_accounts) > 0: def sync_func(account): try: self._mailchecker.check([account]) except: logging.exception('Caught an exception.') idle_timeout = int(self._cfg.get('core', 'imap_idle_timeout')) self._idlrunner = IdlerRunner(idle_accounts, sync_func, idle_timeout) self._idlrunner.start() except Exception as ex: logging.exception('Caught an exception.') if self._fatal_error_handler != None: self._fatal_error_handler(ex) def _wait_for_inet_connection(self): if self._conntest.is_offline(): logging.info('Waiting for internet connection...') while True: if self._disposed: return False if not self._conntest.is_offline(): return True # Note: don't sleep too long # (see timeout in mailnag.cleanup()) # ..but also don't sleep to short in case of a ping connection test. time.sleep(3) def _load_plugins(self, cfg, hookreg, memorizer): class MailnagController_Impl(MailnagController): def __init__(self, daemon, memorizer, hookreg, shutdown_request_hdlr): self._daemon = daemon self._memorizer = memorizer self._hookreg = hookreg self._shutdown_request_handler = shutdown_request_hdlr def get_hooks(self): return self._hookreg def shutdown(self): if self._shutdown_request_handler != None: self._shutdown_request_handler() def check_for_mails(self): self._daemon.check_for_mails() def mark_mail_as_read(self, mail_id): # Note: ensure_valid_state() is not really necessary here # (the memorizer object is available in init() and dispose()), # but better be consistent with other daemon methods. self._daemon._ensure_valid_state() self._memorizer.set_to_seen(mail_id) self._memorizer.save() controller = MailnagController_Impl(self, memorizer, hookreg, self._shutdown_request_handler) enabled_lst = cfg.get('core', 'enabled_plugins').split(',') enabled_lst = filter(lambda s: s != '', map(lambda s: s.strip(), enabled_lst)) self._plugins = Plugin.load_plugins(cfg, controller, enabled_lst) for p in self._plugins: try: p.enable() logging.info("Successfully enabled plugin '%s'." % p.get_modname()) except: logging.error("Failed to enable plugin '%s'." % p.get_modname()) def _unload_plugins(self): if len(self._plugins) > 0: err = False for p in self._plugins: try: p.disable() except: err = True logging.error("Failed to disable plugin '%s'." % p.get_modname()) if not err: logging.info('Plugins disabled successfully.')