diff --git a/Mailnag/common/accounts.py b/Mailnag/common/accounts.py index 74039f1..1cc163c 100644 --- a/Mailnag/common/accounts.py +++ b/Mailnag/common/accounts.py @@ -3,7 +3,7 @@ # # accounts.py # -# Copyright 2011 - 2013 Patrick Ulbrich +# Copyright 2011 - 2014 Patrick Ulbrich # Copyright 2011 Ralf Hersel # # This program is free software; you can redistribute it and/or modify @@ -67,15 +67,35 @@ class Account: return self._get_POP3_connection(use_existing) + # Indicates whether the account + # holds an active existing connection. + # Note: this method only indicates if the + # account *holds* (caches) an existing connection. + # There may be further, but no longer + # associated connections if get_connection() + # was called multiple times (with use_existing + # set to False). + def has_connection(self): + if self.imap: + return self._has_IMAP_connection() + else: + return self._has_POP3_connection() + + def get_id(self): # TODO : this id is not really unique... return str(hash(self.user + self.server + self.folder)) + + def _has_IMAP_connection(self): + return (self._conn != None) and \ + (self._conn.state != imaplib.LOGOUT) and \ + (not self._conn.Terminate) + def _get_IMAP_connection(self, use_existing): # try to reuse existing connection - if use_existing and (self._conn != None) and \ - (self._conn.state != imaplib.LOGOUT) and (not self._conn.Terminate): + if use_existing and self._has_IMAP_connection(): return self._conn self._conn = conn = None @@ -107,9 +127,14 @@ class Account: return self._conn + def _has_POP3_connection(self): + return (self._conn != None) and \ + ('sock' in self._conn.__dict__) + + def _get_POP3_connection(self, use_existing): # try to reuse existing connection - if use_existing and (self._conn != None) and ('sock' in self._conn.__dict__): + if use_existing and self._has_POP3_connection(): return self._conn self._conn = conn = None diff --git a/Mailnag/common/exceptions.py b/Mailnag/common/exceptions.py new file mode 100644 index 0000000..1b49903 --- /dev/null +++ b/Mailnag/common/exceptions.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +# +# exceptions.py +# +# Copyright 2014 Patrick Ulbrich +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +class InvalidOperationException(Exception): + def __init__(self, message): + Exception.__init__(self, message) diff --git a/Mailnag/common/keyring.py b/Mailnag/common/keyring.py index 46b1f15..e8e10c5 100644 --- a/Mailnag/common/keyring.py +++ b/Mailnag/common/keyring.py @@ -3,7 +3,7 @@ # # keyring.py # -# Copyright 2011 - 2013 Patrick Ulbrich +# Copyright 2011 - 2014 Patrick Ulbrich # Copyright 2011 Ralf Hersel # # This program is free software; you can redistribute it and/or modify @@ -26,6 +26,11 @@ from gi.repository import GnomeKeyring from common.i18n import _ +class KeyringUnlockException(Exception): + def __init__(self, message): + Exception.__init__(self, message) + + class Keyring: def __init__(self): self.KEYRING_ITEM_NAME = 'Mailnag password for %s://%s@%s' @@ -39,7 +44,7 @@ class Keyring: result = GnomeKeyring.unlock_sync(self._defaultKeyring, None) if result != GnomeKeyring.Result.OK: - raise Exception('Failed to unlock default keyring') + raise KeyringUnlockException('Failed to unlock default keyring') # get password for account from Gnome Keyring diff --git a/Mailnag/common/plugins.py b/Mailnag/common/plugins.py index fa17b7e..19d2946 100644 --- a/Mailnag/common/plugins.py +++ b/Mailnag/common/plugins.py @@ -3,7 +3,7 @@ # # plugins.py # -# Copyright 2013 Patrick Ulbrich +# Copyright 2013, 2014 Patrick Ulbrich # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -86,29 +86,16 @@ class HookRegistry: # Abstract base class for a MailnagController instance -# passed to plugins +# passed to plugins. class MailnagController: - def __init__(self, hook_registry): - self.hooks = hook_registry - - # - # Abstract methods - # to be implemented by a concrete - # MailnagController implementation - # - - def shutdown(self): - # Expected to shut down the Mailnag daemon. - # Must return True if shutdown succeeded, - # otherwise False. - raise NotImplementedError - - - def check_for_mails(self): - # Expected to check for new mails (non-idle accounts only). - # Must return True if mail checking succeeded, - # otherwise False. - raise NotImplementedError + # Returns a HookRegistry object. + def get_hooks(self): pass + # Shuts down the Mailnag process. + # May throw an InvalidOperationException. + def shutdown(self): pass + # Enforces a manual mail check. + # May throw an InvalidOperationException. + def check_for_mails(self): pass # diff --git a/Mailnag/daemon/idlers.py b/Mailnag/daemon/idlers.py index 42d9d2c..6f34028 100644 --- a/Mailnag/daemon/idlers.py +++ b/Mailnag/daemon/idlers.py @@ -26,8 +26,12 @@ import threading import time import logging from daemon.imaplib2 import AUTH +from common.exceptions import InvalidOperationException +class ConnectionException(Exception): + def __init__(self, message): + Exception.__init__(self, message) # # Idler class # @@ -41,21 +45,22 @@ class Idler(object): self._account = account self._idle_timeout = idle_timeout # use_existing = True: - # connection has been opened in mailnag.py already (immediate check) + # connection has been opened in mailnagdaemon.py already (immediate check) self._conn = account.get_connection(use_existing = True) self._disposed = False if self._conn == None: - raise Exception("Failed to establish a connection for account '%s'" % account.name) + raise ConnectionException( + "Failed to establish a connection for account '%s'" % account.name) # Need to get out of AUTH mode of fresh connections. if self._conn.state == AUTH: self._select(self._conn, account.folder) - def run(self): + def start(self): if self._disposed: - raise Exception("Idler has been disposed") + raise InvalidOperationException("Idler has been disposed") self._thread.start() @@ -67,10 +72,9 @@ class Idler(object): try: if self._conn != None: - # (calls idle_callback) - self._conn.close() - # shutdown existing callback thread - self._conn.logout() + # Exit possible active idle state. + # (also calls idle_callback) + self._conn.noop() except: pass @@ -170,12 +174,12 @@ class IdlerRunner: self._idle_timeout = idle_timeout - def run(self): + def start(self): for acc in self._accounts: if acc.imap and acc.idle: try: idler = Idler(acc, self._sync_callback, self._idle_timeout) - idler.run() + idler.start() self._idlerlist.append(idler) except Exception as ex: logging.error("Error: Failed to create an idler thread for account '%s'" % acc.name) diff --git a/Mailnag/daemon/mailnagdaemon.py b/Mailnag/daemon/mailnagdaemon.py new file mode 100644 index 0000000..76198c2 --- /dev/null +++ b/Mailnag/daemon/mailnagdaemon.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +# +# mailnagdaemon.py +# +# Copyright 2014 Patrick Ulbrich +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +import threading +import logging + +from common.accounts import AccountList +from daemon.mailchecker import MailChecker +from daemon.idlers import IdlerRunner +from common.plugins import Plugin, HookRegistry, MailnagController +from common.exceptions import InvalidOperationException +from common.config import read_cfg + +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._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() + self._accounts = AccountList() + self._accounts.load_from_cfg(self._cfg, enabled_only = True) + + hook_registry = HookRegistry() + + self._mailchecker = MailChecker(self._cfg, hook_registry) + + # 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(hook_registry) + + # 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. + 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.has_connection(): + if acc.imap: + conn = acc.get_connection(use_existing = True) + conn.close() + conn.logout() + else: + conn.quit() + + 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() + self._mailchecker.check(self._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: + # 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 _load_plugins(self, hookreg): + class MailnagController_Impl(MailnagController): + def __init__(self, daemon, hookreg, shutdown_request_hdlr): + self._daemon = daemon + 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() + + controller = MailnagController_Impl(self, hookreg, self._shutdown_request_handler) + + enabled_lst = self._cfg.get('core', 'enabled_plugins').split(',') + enabled_lst = filter(lambda s: s != '', map(lambda s: s.strip(), enabled_lst)) + self._plugins = Plugin.load_plugins(self._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.') + + diff --git a/Mailnag/mailnag.py b/Mailnag/mailnag.py index 80df59b..ba70ee6 100644 --- a/Mailnag/mailnag.py +++ b/Mailnag/mailnag.py @@ -33,13 +33,11 @@ import os import time import signal -from common.config import read_cfg, cfg_exists, cfg_folder +from common.config import cfg_exists from common.utils import set_procname, is_online, shutdown_existing_instance -from common.accounts import AccountList -from common.plugins import Plugin, HookRegistry, MailnagController from common.subproc import terminate_subprocesses -from daemon.mailchecker import MailChecker -from daemon.idlers import IdlerRunner +from common.exceptions import InvalidOperationException +from daemon.mailnagdaemon import MailnagDaemon PROGNAME = 'mailnagd' @@ -47,43 +45,6 @@ LOG_LEVEL = logging.DEBUG LOG_FORMAT = '%(levelname)s (%(asctime)s): %(message)s' LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S' -mainloop = None -idlrunner = None -plugins = [] -hook_registry = HookRegistry() -start_thread = None -poll_thread = None -poll_thread_stop = threading.Event() - - -# Mailnag controller implementation passed to plugins -class MailnagController_Impl(MailnagController): - def __init__(self, hookreg): - MailnagController.__init__(self, hookreg) - - - def shutdown(self): - if mainloop != None: - mainloop.quit() - return True - else: - return False - - - def check_for_mails(self): - if (mailchecker != None): #and (non_idle_accounts != None): - # TODO mailchecker.check(non_idle_accounts) - return True - else: - return False - - -def read_config(): - if not cfg_exists(): - return None - else: - return read_cfg() - def wait_for_inet_connection(): if not is_online(): @@ -92,30 +53,12 @@ def wait_for_inet_connection(): time.sleep(5) -def cleanup(): +def cleanup(daemon): event = threading.Event() def thread(): - # clean up resources - if (start_thread != None) and (start_thread.is_alive()): - start_thread.join() - logging.info('Starter thread exited successfully.') - - if (poll_thread != None) and (poll_thread.is_alive()): - poll_thread_stop.set() - poll_thread.join() - logging.info('Polling thread exited successfully.') - - if idlrunner != None: - idlrunner.dispose() - - # Clear vars used in the MailnagController - # so plugins can't perform unwanted calls to - # shutdown() or check_for_mail() - # when being disabled on shut down. - mainloop = None - mailchecker = None - - unload_plugins() + if daemon != None: + daemon.dispose() + terminate_subprocesses(timeout = 3.0) event.set() @@ -159,55 +102,25 @@ def init_logging(enable_stdout = True): logger.removeHandler(stdout_handler) -def sigterm_handler(data): +def sigterm_handler(mainloop): if mainloop != None: mainloop.quit() -def load_plugins(cfg, hookreg): - global plugins - controller = MailnagController_Impl(hookreg) - - enabled_lst = cfg.get('core', 'enabled_plugins').split(',') - enabled_lst = filter(lambda s: s != '', map(lambda s: s.strip(), enabled_lst)) - plugins = Plugin.load_plugins(cfg, controller, enabled_lst) - - for p in 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(): - if len(plugins) > 0: - err = False - - for p in 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.') - - def main(): - global mainloop, start_thread + mainloop = GObject.MainLoop() + daemon = None set_procname(PROGNAME) GObject.threads_init() DBusGMainLoop(set_as_default = True) GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, - sigterm_handler, None) + sigterm_handler, mainloop) # Get commandline arguments args = get_args() - # shut down an (possibly) already running Mailnag daemon + # Shut down an (possibly) already running Mailnag daemon # (must be called before instantiation of the DBUSService). shutdown_existing_instance() @@ -216,84 +129,40 @@ def main(): init_logging(not args.quiet) try: - cfg = read_config() - - if cfg == None: - logging.critical('Cannot find configuration file. Please run mailnag-config first.') + if not cfg_exists(): + logging.critical( + "Cannot find configuration file. " + \ + "Please run mailnag-config first.") exit(1) wait_for_inet_connection() - load_plugins(cfg, hook_registry) - - # start checking for mails asynchronously - start_thread = threading.Thread(target = start, args = (cfg, hook_registry, )) - start_thread.start() + def fatal_error_hdlr(ex): + # Note: don't raise an exception + # (e.g InvalidOperationException) + # in the error handler. + mainloop.quit() + + def shutdown_request_hdlr(): + if not mainloop.is_running(): + raise InvalidOperationException( + "Mainloop is not running") + mainloop.quit() + daemon = MailnagDaemon( + fatal_error_hdlr, + shutdown_request_hdlr) + + daemon.init() + # start mainloop for DBus communication - mainloop = GObject.MainLoop() mainloop.run() except KeyboardInterrupt: pass # ctrl+c pressed finally: logging.info('Shutting down...') - cleanup() + cleanup(daemon) -def start(cfg, hookreg): - global poll_thread, idlrunner - - try: - accounts = AccountList() - accounts.load_from_cfg(cfg, enabled_only = True) - - mailchecker = MailChecker(cfg, hookreg) - - # immediate check, check *all* accounts - try: - mailchecker.check(accounts) - except: - logging.exception('Caught an exception.') - - idle_accounts = filter(lambda acc: acc.imap and acc.idle, accounts) - non_idle_accounts = filter(lambda acc: (not acc.imap) or (acc.imap and not acc.idle), accounts) - - # start polling thread for POP3 accounts and - # IMAP accounts without idle support - if len(non_idle_accounts) > 0: - poll_interval = int(cfg.get('core', 'poll_interval')) - - def poll_func(): - try: - while True: - poll_thread_stop.wait(timeout = 60.0 * poll_interval) - if poll_thread_stop.is_set(): - break - - mailchecker.check(non_idle_accounts) - except: - logging.exception('Caught an exception.') - - poll_thread = threading.Thread(target = poll_func) - poll_thread.start() - - - # start idler threads for IMAP accounts with idle support - if len(idle_accounts) > 0: - def sync_func(account): - try: - mailchecker.check([account]) - except: - logging.exception('Caught an exception.') - - - idle_timeout = int(cfg.get('core', 'imap_idle_timeout')) - idlrunner = IdlerRunner(idle_accounts, sync_func, idle_timeout) - idlrunner.run() - except: - logging.exception('Caught an exception.') - mainloop.quit() - - if __name__ == '__main__': main() diff --git a/Mailnag/plugins/dbusplugin.py b/Mailnag/plugins/dbusplugin.py index fe7579f..4fbb512 100644 --- a/Mailnag/plugins/dbusplugin.py +++ b/Mailnag/plugins/dbusplugin.py @@ -3,7 +3,7 @@ # # dbusplugin.py # -# Copyright 2013 Patrick Ulbrich +# Copyright 2013, 2014 Patrick Ulbrich # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,6 +25,7 @@ import dbus import dbus.service from common.dist_cfg import DBUS_BUS_NAME, DBUS_OBJ_PATH from common.plugins import Plugin, HookTypes +from common.exceptions import InvalidOperationException from common.i18n import _ plugin_defaults = {} @@ -39,6 +40,7 @@ class DBusPlugin(Plugin): def enable(self): controller = self.get_mailnag_controller() + hooks = controller.get_hooks() self._dbusservice = DBusService(controller) def mails_added_hook(new_mails, all_mails): @@ -55,23 +57,24 @@ class DBusPlugin(Plugin): self._mails_added_hook = mails_added_hook self._mails_removed_hook = mails_removed_hook - controller.hooks.register_hook_func(HookTypes.MAILS_ADDED, + hooks.register_hook_func(HookTypes.MAILS_ADDED, self._mails_added_hook) - controller.hooks.register_hook_func(HookTypes.MAILS_REMOVED, + hooks.register_hook_func(HookTypes.MAILS_REMOVED, self._mails_removed_hook) def disable(self): self._dbusservice = None controller = self.get_mailnag_controller() + hooks = controller.get_hooks() if self._mails_added_hook != None: - controller.hooks.unregister_hook_func(HookTypes.MAILS_ADDED, + hooks.unregister_hook_func(HookTypes.MAILS_ADDED, self._mails_added_hook) self._mails_added_hook = None if self._mails_removed_hook != None: - controller.hooks.unregister_hook_func(HookTypes.MAILS_REMOVED, + hooks.unregister_hook_func(HookTypes.MAILS_REMOVED, self._mails_removed_hook) self._mails_removed_hook = None @@ -156,5 +159,16 @@ class DBusService(dbus.service.Object): @dbus.service.method(dbus_interface = DBUS_BUS_NAME) def Shutdown(self): - self._mailnag_controller.shutdown() + try: + self._mailnag_controller.shutdown() + except InvalidOperationException: + pass + + + @dbus.service.method(dbus_interface = DBUS_BUS_NAME) + def CheckForMails(self): + try: + self._mailnag_controller.check_for_mails() + except InvalidOperationException: + pass diff --git a/Mailnag/plugins/libnotifyplugin.py b/Mailnag/plugins/libnotifyplugin.py index 25682b1..8e54c9b 100644 --- a/Mailnag/plugins/libnotifyplugin.py +++ b/Mailnag/plugins/libnotifyplugin.py @@ -3,7 +3,7 @@ # # libnotifyplugin.py # -# Copyright 2013 Patrick Ulbrich +# Copyright 2013, 2014 Patrick Ulbrich # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -73,23 +73,25 @@ class LibNotifyPlugin(Plugin): self._mails_removed_hook = mails_removed_hook controller = self.get_mailnag_controller() + hooks = controller.get_hooks() - controller.hooks.register_hook_func(HookTypes.MAILS_ADDED, + hooks.register_hook_func(HookTypes.MAILS_ADDED, self._mails_added_hook) - controller.hooks.register_hook_func(HookTypes.MAILS_REMOVED, + hooks.register_hook_func(HookTypes.MAILS_REMOVED, self._mails_removed_hook) def disable(self): controller = self.get_mailnag_controller() + hooks = controller.get_hooks() if self._mails_added_hook != None: - controller.hooks.unregister_hook_func(HookTypes.MAILS_ADDED, + hooks.unregister_hook_func(HookTypes.MAILS_ADDED, self._mails_added_hook) self._mails_added_hook = None if self._mails_removed_hook != None: - controller.hooks.unregister_hook_func(HookTypes.MAILS_REMOVED, + hooks.unregister_hook_func(HookTypes.MAILS_REMOVED, self._mails_removed_hook) self._mails_removed_hook = None diff --git a/Mailnag/plugins/soundplugin.py b/Mailnag/plugins/soundplugin.py index 732d641..0bd1f9d 100644 --- a/Mailnag/plugins/soundplugin.py +++ b/Mailnag/plugins/soundplugin.py @@ -3,7 +3,7 @@ # # soundplugin.py # -# Copyright 2013 Patrick Ulbrich +# Copyright 2013, 2014 Patrick Ulbrich # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -44,16 +44,18 @@ class SoundPlugin(Plugin): self._mails_added_hook = mails_added_hook controller = self.get_mailnag_controller() + hooks = controller.get_hooks() - controller.hooks.register_hook_func(HookTypes.MAILS_ADDED, + hooks.register_hook_func(HookTypes.MAILS_ADDED, self._mails_added_hook) def disable(self): controller = self.get_mailnag_controller() + hooks = controller.get_hooks() if self._mails_added_hook != None: - controller.hooks.unregister_hook_func(HookTypes.MAILS_ADDED, + hooks.unregister_hook_func(HookTypes.MAILS_ADDED, self._mails_added_hook) self._mails_added_hook = None diff --git a/Mailnag/plugins/spamfilterplugin.py b/Mailnag/plugins/spamfilterplugin.py index ab94751..2c5e58f 100644 --- a/Mailnag/plugins/spamfilterplugin.py +++ b/Mailnag/plugins/spamfilterplugin.py @@ -3,7 +3,7 @@ # # smamfilterplugin.py # -# Copyright 2013 Patrick Ulbrich +# Copyright 2013, 2014 Patrick Ulbrich # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -47,16 +47,18 @@ class SpamfilterPlugin(Plugin): self._filter_mails_hook = filter_mails_hook controller = self.get_mailnag_controller() + hooks = controller.get_hooks() - controller.hooks.register_hook_func(HookTypes.FILTER_MAILS, + hooks.register_hook_func(HookTypes.FILTER_MAILS, self._filter_mails_hook) def disable(self): controller = self.get_mailnag_controller() + hooks = controller.get_hooks() if self._filter_mails_hook != None: - controller.hooks.unregister_hook_func(HookTypes.FILTER_MAILS, + hooks.unregister_hook_func(HookTypes.FILTER_MAILS, self._filter_mails_hook) self._filter_mails_hook = None diff --git a/Mailnag/plugins/userscriptplugin.py b/Mailnag/plugins/userscriptplugin.py index df5fb17..be7c409 100644 --- a/Mailnag/plugins/userscriptplugin.py +++ b/Mailnag/plugins/userscriptplugin.py @@ -3,7 +3,7 @@ # # userscriptplugin.py # -# Copyright 2013 Patrick Ulbrich +# Copyright 2013, 2014 Patrick Ulbrich # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -42,16 +42,18 @@ class UserscriptPlugin(Plugin): self._mails_added_hook = mails_added_hook controller = self.get_mailnag_controller() + hooks = controller.get_hooks() - controller.hooks.register_hook_func(HookTypes.MAILS_ADDED, + hooks.register_hook_func(HookTypes.MAILS_ADDED, self._mails_added_hook) def disable(self): controller = self.get_mailnag_controller() + hooks = controller.get_hooks() if self._mails_added_hook != None: - controller.hooks.unregister_hook_func(HookTypes.MAILS_ADDED, + hooks.unregister_hook_func(HookTypes.MAILS_ADDED, self._mails_added_hook) self._mails_added_hook = None