From b6cbf7b9dec2b3cfd806bfaaeef4a021ae00d010 Mon Sep 17 00:00:00 2001 From: Timo Kankare Date: Sun, 4 Sep 2016 22:54:39 +0300 Subject: [PATCH] Moved IMAP and POP3 related code to backends from account module. Added backend creation to account dialog so that the folder request can be done. --- Mailnag/backends/imap.py | 92 +++++++++++++++++++- Mailnag/backends/pop3.py | 66 ++++++++++++++- Mailnag/common/accounts.py | 112 ++----------------------- Mailnag/configuration/accountdialog.py | 9 ++ 4 files changed, 170 insertions(+), 109 deletions(-) diff --git a/Mailnag/backends/imap.py b/Mailnag/backends/imap.py index 416eec6..cc2662f 100644 --- a/Mailnag/backends/imap.py +++ b/Mailnag/backends/imap.py @@ -23,6 +23,94 @@ # MA 02110-1301, USA. # -class ImapBackend: - pass +import logging +import re +import Mailnag.common.imaplib2 as imaplib + +class ImapBackend: + """Implementation of IMAP mail boxes.""" + + def __init__(self, name = '', user = '', password = '', oauth2string = '', + server = '', port = '', ssl = True, folders = []): + self.name = name + self.user = user + self.password = password + self.oauth2string = oauth2string + self.server = server + self.port = port + self.ssl = ssl # bool + self.folders = folders + self._conn = None + + + def get_connection(self, use_existing): + # try to reuse existing connection + if use_existing and self.has_connection(): + return self._conn + + self._conn = conn = None + + try: + if self.ssl: + if self.port == '': + conn = imaplib.IMAP4_SSL(self.server) + else: + conn = imaplib.IMAP4_SSL(self.server, int(self.port)) + else: + if self.port == '': + conn = imaplib.IMAP4(self.server) + else: + conn = imaplib.IMAP4(self.server, int(self.port)) + + if 'STARTTLS' in conn.capabilities: + conn.starttls() + else: + logging.warning("Using unencrypted connection for account '%s'" % self.name) + + if self.oauth2string != '': + conn.authenticate('XOAUTH2', lambda x: self.oauth2string) + else: + conn.login(self.user, self.password) + + self._conn = conn + except: + try: + if conn != None: + # conn.close() # allowed in SELECTED state only + conn.logout() + except: pass + raise # re-throw exception + + return self._conn + + + def has_connection(self): + return (self._conn != None) and \ + (self._conn.state != imaplib.LOGOUT) and \ + (not self._conn.Terminate) + + + def request_folders(self): + lst = [] + + # Always create a new connection as an existing one may + # be used for IMAP IDLE. + conn = self.get_connection(use_existing = False) + + try: + status, data = conn.list('', '*') + finally: + # conn.close() # allowed in SELECTED state only + conn.logout() + + for d in data: + match = re.match('.+\s+("."|"?NIL"?)\s+"?([^"]+)"?$', d) + + if match == None: + logging.warning("Folder format not supported.") + else: + folder = match.group(2) + lst.append(folder) + + return lst diff --git a/Mailnag/backends/pop3.py b/Mailnag/backends/pop3.py index 57cb48c..9651839 100644 --- a/Mailnag/backends/pop3.py +++ b/Mailnag/backends/pop3.py @@ -23,6 +23,68 @@ # MA 02110-1301, USA. # -class Pop3Backend: - pass +import logging +import poplib + +class Pop3Backend: + """Implementation of POP3 mail boxes.""" + + def __init__(self, name = '', user = '', password = '', oauth2string = '', + server = '', port = '', ssl = True): + self.name = name + self.user = user + self.password = password + self.oauth2string = oauth2string + self.server = server + self.port = port + self.ssl = ssl # bool + self._conn = None + + + def get_connection(self, use_existing): + # try to reuse existing connection + if use_existing and self.has_connection(): + return self._conn + + self._conn = conn = None + + try: + if self.ssl: + if self.port == '': + conn = poplib.POP3_SSL(self.server) + else: + conn = poplib.POP3_SSL(self.server, int(self.port)) + else: + if self.port == '': + conn = poplib.POP3(self.server) + else: + conn = poplib.POP3(self.server, int(self.port)) + + # TODO : Use STARTTLS when Mailnag has been migrated to python 3 + # (analogous to get_connection in imap backend). + logging.warning("Using unencrypted connection for account '%s'" % self.name) + + conn.getwelcome() + conn.user(self.user) + conn.pass_(self.password) + + self._conn = conn + except: + try: + if conn != None: + conn.quit() + except: pass + raise # re-throw exception + + return self._conn + + + def has_connection(self): + return (self._conn != None) and \ + ('sock' in self._conn.__dict__) + + + def request_folders(self): + lst = [] + return lst diff --git a/Mailnag/common/accounts.py b/Mailnag/common/accounts.py index 9659ea4..cf57eaf 100644 --- a/Mailnag/common/accounts.py +++ b/Mailnag/common/accounts.py @@ -24,10 +24,8 @@ # import re -import poplib import logging import json -import Mailnag.common.imaplib2 as imaplib from Mailnag.backends.imap import ImapBackend from Mailnag.backends.pop3 import Pop3Backend from Mailnag.common.utils import splitstr @@ -95,31 +93,7 @@ class Account: # Relevant for IMAP accounts only. # Returns an empty list when used on POP3 accounts. def request_server_folders(self): - lst = [] - - if not self.imap: - return lst - - # Always create a new connection as an existing one may - # be used for IMAP IDLE. - conn = self._get_IMAP_connection(use_existing = False) - - try: - status, data = conn.list('', '*') - finally: - # conn.close() # allowed in SELECTED state only - conn.logout() - - for d in data: - match = re.match('.+\s+("."|"?NIL"?)\s+"?([^"]+)"?$', d) - - if match == None: - logging.warning("Folder format not supported.") - else: - folder = match.group(2) - lst.append(folder) - - return lst + return self.backend.request_folders() def get_id(self): @@ -128,92 +102,20 @@ class Account: def _has_IMAP_connection(self): - return (self._conn != None) and \ - (self._conn.state != imaplib.LOGOUT) and \ - (not self._conn.Terminate) + return self.backend.has_connection() def _get_IMAP_connection(self, use_existing): - # try to reuse existing connection - if use_existing and self._has_IMAP_connection(): - return self._conn - - self._conn = conn = None - - try: - if self.ssl: - if self.port == '': - conn = imaplib.IMAP4_SSL(self.server) - else: - conn = imaplib.IMAP4_SSL(self.server, int(self.port)) - else: - if self.port == '': - conn = imaplib.IMAP4(self.server) - else: - conn = imaplib.IMAP4(self.server, int(self.port)) - - if 'STARTTLS' in conn.capabilities: - conn.starttls() - else: - logging.warning("Using unencrypted connection for account '%s'" % self.name) - - if self.oauth2string != '': - conn.authenticate('XOAUTH2', lambda x: self.oauth2string) - else: - conn.login(self.user, self.password) - - self._conn = conn - except: - try: - if conn != None: - # conn.close() # allowed in SELECTED state only - conn.logout() - except: pass - raise # re-throw exception - + self._conn = self.backend.get_connection(use_existing) return self._conn def _has_POP3_connection(self): - return (self._conn != None) and \ - ('sock' in self._conn.__dict__) + return self.backend.has_connection() def _get_POP3_connection(self, use_existing): - # try to reuse existing connection - if use_existing and self._has_POP3_connection(): - return self._conn - - self._conn = conn = None - - try: - if self.ssl: - if self.port == '': - conn = poplib.POP3_SSL(self.server) - else: - conn = poplib.POP3_SSL(self.server, int(self.port)) - else: - if self.port == '': - conn = poplib.POP3(self.server) - else: - conn = poplib.POP3(self.server, int(self.port)) - - # TODO : Use STARTTLS when Mailnag has been migrated to python 3 - # (analogous to get_IMAP_connection). - logging.warning("Using unencrypted connection for account '%s'" % self.name) - - conn.getwelcome() - conn.user(self.user) - conn.pass_(self.password) - - self._conn = conn - except: - try: - if conn != None: - conn.quit() - except: pass - raise # re-throw exception - + self._conn = self.backend.get_connection(use_existing) return self._conn @@ -291,9 +193,9 @@ class AccountManager: password = self._credentialstore.get(CREDENTIAL_KEY % (protocol, user, server)) if imap: - backend = ImapBackend() + backend = ImapBackend(name, user, password, '', server, port, ssl, folders) else: - bakcend = Pop3Backend() + backend = Pop3Backend(name, user, password, '', server, port, ssl) acc = Account(enabled, name, user, password, '', server, port, ssl, imap, idle, folders, backend) self._accounts.append(acc) diff --git a/Mailnag/configuration/accountdialog.py b/Mailnag/configuration/accountdialog.py index 4ad5596..4484806 100644 --- a/Mailnag/configuration/accountdialog.py +++ b/Mailnag/configuration/accountdialog.py @@ -27,6 +27,8 @@ gi.require_version('GLib', '2.0') from gi.repository import GObject, GLib, Gtk from thread import start_new_thread +from Mailnag.backends.imap import ImapBackend +from Mailnag.backends.pop3 import Pop3Backend from Mailnag.common.dist_cfg import PACKAGE_NAME from Mailnag.common.i18n import _ from Mailnag.common.utils import get_data_file, splitstr @@ -165,6 +167,13 @@ class AccountDialog: acc.port = p[2] else: raise Exception('Unknown account type') + + # Create backend + # TODO: This is duplicate code with AccountManager. + if acc.imap: + acc.backend = ImapBackend(acc.name, acc.user, acc.password, '', acc.server, acc.port, acc.ssl, acc.folders) + else: + acc.backend = Pop3Backend(acc.name, acc.user, acc.password, '', acc.server, acc.port, acc.ssl) def _get_selected_folders(self):