def _test_login_and_proto(self, email, settings): event = Event(data={}) if settings['protocol'].startswith('smtp'): try: assert(SendMail(self.session, None, [(email, [email, '*****@*****.**'], None, [event])], test_only=True, test_route=settings)) return True, True except (IOError, OSError, AssertionError, SendMailError): pass if settings['protocol'].startswith('imap'): from mailpile.mail_source.imap import TestImapSettings if TestImapSettings(self.session, settings, event): return True, True if settings['protocol'].startswith('pop3'): from mailpile.mail_source.pop3 import TestPop3Settings if TestPop3Settings(self.session, settings, event): return True, True if ('connection' in event.data and event.data['connection']['error'][0] == 'auth'): return False, True if ('last_error' in event.data and event.data.get('auth')): return False, True return False, False
def __init__(self, session=None, config=None, socks_port=None, control_port=None, tor_binary=None, callbacks=None): threading.Thread.__init__(self) self.session = session self.config = config or (session.config if session else None) self.callbacks = callbacks if self.config is None: self.socks_port = None self.control_port = None self.control_password = okay_random(32) self.tor_binary = tor_binary else: self.socks_port = self.config.sys.tor.socks_port self.control_port = self.config.sys.tor.ctrl_port self.control_password = self.config.sys.tor.ctrl_auth self.tor_binary = tor_binary or self.config.sys.tor.binary or None if socks_port is not None: self.socks_port = socks_port if control_port is not None: self.control_port = control_port self.event = Event(source=self, flags=Event.INCOMPLETE, data={}) self.lock = threading.Lock() self.tor_process = None self.tor_controller = None self.hidden_services = {} self.keep_looping = True self.started = False
def _create_new_key(self, vcard, keytype): passphrase = okay_random(26, self.session.config.master_key ).lower() random_uid = vcard.random_uid bits = int(keytype.replace('RSA', '')) key_args = { # FIXME: EC keys! 'bits': bits, 'name': vcard.fn, 'email': vcard.email, 'passphrase': passphrase, 'comment': '' } event = Event(source=self, message=_('Generating new %d bit PGP key. ' 'This may take some time!') % bits, flags=Event.INCOMPLETE, data={'keygen_started': int(time.time()), 'profile_id': random_uid}, private_data=key_args) self._key_generator = GnuPGKeyGenerator( # FIXME: Passphrase handling is a problem here variables=dict_merge(GnuPGKeyGenerator.VARIABLES, key_args), on_complete=(random_uid, lambda: self._new_key_created(event, random_uid, passphrase)) ) self._key_generator.start() self.session.config.event_log.log_event(event)
def _create_event(self): private_data = {} if self.data: private_data['data'] = copy.copy(self.data) if self.args: private_data['args'] = copy.copy(self.args) self.event = Event(source=self, message=self._fmt_msg(self.LOG_STARTING), data={}, private_data=private_data)
def open(self, conn_cls=None, throw=False): conn = self.conn conn_id = self._conn_id() if conn: try: with conn as c: now = time.time() if (conn_id == self.conn_id and (now < self.last_op + 120 or self.timed(c.noop)[0] == 'OK')): # Make the timeout longer, so we don't drop things # on every hiccup and so downloads will be more # efficient (chunk size relates to timeout). self.timeout = self.TIMEOUT_LIVE if now >= self.last_op + 120: self.last_op = now return conn except self.CONN_ERRORS + (AttributeError, ): pass with self._lock: if self.conn == conn: self.conn = None conn.quit() my_config = self.my_config # This facilitates testing, event should already exist in real life. if self.event: event = self.event else: event = Event(source=self, flags=Event.RUNNING, data={}) def logged_in_cb(conn, ev, capabilities): with self._lock: if self.conn is not None: raise IOError('Woah, we lost a race.') self.capabilities = capabilities if 'IDLE' in capabilities: self.conn = SharedImapConn( self.session, conn, idle_mailbox='INBOX', idle_callback=self._idle_callback) else: self.conn = SharedImapConn(self.session, conn) if 'NAMESPACE' in capabilities: ok, data = self.timed_imap(conn.namespace) if ok: prv, oth, shr = data self.namespaces = { 'private': prv if (prv != 'NIL') else [], 'others': oth if (oth != 'NIL') else [], 'shared': shr if (shr != 'NIL') else [] } if self.event: self._log_status(_('Connected to IMAP server %s' ) % my_config.host) if 'imap' in self.session.config.sys.debug: self.session.ui.debug('CONNECTED %s' % self.conn) self.session.ui.debug('CAPABILITIES %s' % self.capabilities) self.session.ui.debug('NAMESPACES %s' % self.namespaces) self.conn_id = conn_id ev['live'] = True conn = _connect_imap(self.session, self.my_config, event, conn_cls=conn_cls, timeout=self.timeout, throw=throw, logged_in_cb=logged_in_cb) if conn: return self.conn else: return WithaBool(False)
def open(self, conn_cls=None, throw=False): conn = self.conn conn_id = self._conn_id() if conn: try: with conn as c: if (conn_id == self.conn_id and self.timed(c.noop)[0] == 'OK'): # Make the timeout longer, so we don't drop things # on every hiccup and so downloads will be more # efficient (chunk size relates to timeout). self.timeout = self.TIMEOUT_LIVE return conn except self.CONN_ERRORS + (AttributeError, ): pass with self._lock: if self.conn == conn: self.conn = None conn.quit() # This facilitates testing, event should already exist in real life. if self.event: event = self.event else: event = Event(source=self, flags=Event.RUNNING, data={}) # Prepare the data section of our event, for keeping state. for d in ('mailbox_state',): if d not in event.data: event.data[d] = {} ev = event.data['connection'] = { 'live': False, 'error': [False, _('Nothing is wrong')] } conn = None my_config = self.my_config mailboxes = my_config.mailbox.values() # If we are given a conn class, use that - this allows mocks for # testing. if not conn_cls: want_ssl = (my_config.protocol == 'imap_ssl') conn_cls = IMAP4_SSL if want_ssl else IMAP4 try: def mkconn(): with ConnBroker.context(need=[ConnBroker.OUTGOING_IMAP]): return conn_cls(my_config.host, my_config.port) conn = self.timed(mkconn) conn.debug = ('imaplib' in self.session.config.sys.debug ) and 4 or 0 ok, data = self.timed_imap(conn.capability) if ok: capabilities = set(' '.join(data).upper().split()) else: capabilities = set() #if 'STARTTLS' in capabilities and not want_ssl: # # FIXME: We need to send a STARTTLS and do a switcheroo where # the connection gets encrypted. try: ok, data = self.timed_imap(conn.login, my_config.username, my_config.password) except IMAP4.error: ok = False if not ok: ev['error'] = ['auth', _('Invalid username or password')] if throw: raise throw(event.data['conn_error']) return WithaBool(False) with self._lock: if self.conn is not None: raise IOError('Woah, we lost a race.') self.capabilities = capabilities if 'IDLE' in capabilities: self.conn = SharedImapConn( self.session, conn, idle_mailbox='INBOX', idle_callback=self._idle_callback) else: self.conn = SharedImapConn(self.session, conn) if self.event: self._log_status(_('Connected to IMAP server %s' ) % my_config.host) if 'imap' in self.session.config.sys.debug: self.session.ui.debug('CONNECTED %s' % self.conn) self.conn_id = conn_id ev['live'] = True return self.conn except TimedOut: if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['timeout', _('Connection timed out')] except (IMAP_IOError, IMAP4.error): if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['protocol', _('An IMAP protocol error occurred')] except (IOError, AttributeError, socket.error): if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['network', _('A network error occurred')] try: if conn: # Close the socket directly, in the hopes this will boot # any timed-out operations out of a hung state. conn.socket().shutdown(socket.SHUT_RDWR) conn.file.close() except (AttributeError, IOError, socket.error): pass if throw: raise throw(ev['error']) return WithaBool(False)
def _unlocked_open(self, conn_cls=None, throw=False): # # When opening an IMAP connection, we need to do a few things: # 1. Connect, log in # 2. Check the capabilities of the remote server # 3. If there is IDLE support, subscribe to all the paths we # are currently watching. my_config = self.my_config mailboxes = my_config.mailbox.values() if self.conn: try: with self.conn as conn: if self.timed(conn.noop)[0] == 'OK': return self.conn except self.CONN_ERRORS: self.conn.quit() conn = self.conn = None # If we are given a conn class, use that - this allows mocks for # testing. if not conn_cls: want_ssl = (my_config.protocol == 'imap_ssl') conn_cls = IMAP4_SSL if want_ssl else IMAP4 # This also facilitates testing, should already exist in real life. if self.event: event = self.event else: event = Event(source=self, flags=Event.RUNNING, data={}) if 'conn_error' in event.data: del event.data['conn_error'] try: def mkconn(): return conn_cls(my_config.host, my_config.port) conn = self.timed(mkconn) ok, data = self.timed_imap(conn.login, my_config.username, my_config.password) if not ok: event.data['conn_error'] = _('Bad username or password') if throw: raise throw(event.data['conn_error']) return False ok, data = self.timed_imap(conn.capability) if ok: self.capabilities = set(' '.join(data).upper().split()) else: self.capabilities = set() if 'IDLE' in self.capabilities: self.conn = SharedImapConn(conn, idle_mailbox='INBOX', idle_callback=self._idle_callback) else: self.conn = SharedImapConn(conn) # Prepare the data section of our event, for keeping state. for d in ('uidvalidity', 'uidnext'): if d not in event.data: event.data[d] = {} if self.event: self._log_status(_('Connected to IMAP server %s' ) % my_config.host) if DEBUG_IMAP: print 'CONNECTED %s' % self.conn return self.conn except TimedOut: event.data['conn_error'] = _('Connection timed out') except (IMAP_IOError, IMAP4.error): if DEBUG_IMAP: traceback.print_exc() event.data['conn_error'] = _('An IMAP protocol error occurred') except (IOError, AttributeError, socket.error): if DEBUG_IMAP: traceback.print_exc() event.data['conn_error'] = _('A network error occurred') try: if conn: # Close the socket directly, in the hopes this will boot # any timed-out operations out of a hung state. conn.socket().shutdown(socket.SHUT_RDWR) conn.file.close() except (AttributeError, IOError, socket.error): pass if throw: raise throw(event.data['conn_error']) return False
def _make_command_event(self, private_data): return Event(source=self, message=self._fmt_msg(self.LOG_STARTING), flags=Event.INCOMPLETE, data={}, private_data=private_data)
def open(self, conn_cls=None, throw=False): conn = self.conn conn_id = self._conn_id() if conn: try: with conn as c: now = time.time() if (conn_id == self.conn_id and (now < self.last_op + 120 or self.timed(c.noop)[0] == 'OK')): # Make the timeout longer, so we don't drop things # on every hiccup and so downloads will be more # efficient (chunk size relates to timeout). self.timeout = self.TIMEOUT_LIVE if now >= self.last_op + 120: self.last_op = now return conn except self.CONN_ERRORS + (AttributeError, ): pass with self._lock: if self.conn == conn: self.conn = None conn.quit() # This facilitates testing, event should already exist in real life. if self.event: event = self.event else: event = Event(source=self, flags=Event.RUNNING, data={}) # Prepare the data section of our event, for keeping state. for d in ('mailbox_state',): if d not in event.data: event.data[d] = {} ev = event.data['connection'] = { 'live': False, 'error': [False, _('Nothing is wrong')] } conn = None my_config = self.my_config mailboxes = my_config.mailbox.values() # If we are given a conn class, use that - this allows mocks for # testing. if not conn_cls: req_stls = (my_config.protocol == 'imap_tls') want_ssl = (my_config.protocol == 'imap_ssl') conn_cls = IMAP4_SSL if want_ssl else IMAP4 else: req_stls = want_ssl = False try: def mkconn(): with ConnBroker.context(need=[ConnBroker.OUTGOING_IMAP]): return conn_cls(my_config.host, my_config.port) conn = self.timed(mkconn) conn.debug = ('imaplib' in self.session.config.sys.debug ) and 4 or 0 ok, data = self.timed_imap(conn.capability) if ok: capabilities = set(' '.join(data).upper().split()) else: capabilities = set() if req_stls or ('STARTTLS' in capabilities and not want_ssl): try: ok, data = self.timed_imap(conn.starttls) if ok: # Fetch capabilities again after STARTTLS ok, data = self.timed_imap(conn.capability) capabilities = set(' '.join(data).upper().split()) except (IMAP4.error, IOError, socket.error): ok = False if not ok: ev['error'] = ['protocol', _('Failed to STARTTLS')] if throw: raise throw(ev['error'][1]) return WithaBool(False) try: ok, data = self.timed_imap(conn.login, my_config.username, my_config.password) except IMAP4.error: ok = False if not ok: ev['error'] = ['auth', _('Invalid username or password')] if throw: raise throw(ev['error'][1]) return WithaBool(False) with self._lock: if self.conn is not None: raise IOError('Woah, we lost a race.') self.capabilities = capabilities if 'IDLE' in capabilities: self.conn = SharedImapConn( self.session, conn, idle_mailbox='INBOX', idle_callback=self._idle_callback) else: self.conn = SharedImapConn(self.session, conn) if 'NAMESPACE' in capabilities: ok, data = self.timed_imap(conn.namespace) if ok: prv, oth, shr = data self.namespaces = { 'private': prv if (prv != 'NIL') else [], 'others': oth if (oth != 'NIL') else [], 'shared': shr if (shr != 'NIL') else [] } if self.event: self._log_status(_('Connected to IMAP server %s' ) % my_config.host) if 'imap' in self.session.config.sys.debug: self.session.ui.debug('CONNECTED %s' % self.conn) self.session.ui.debug('CAPABILITIES %s' % self.capabilities) self.session.ui.debug('NAMESPACES %s' % self.namespaces) self.conn_id = conn_id ev['live'] = True return self.conn except TimedOut: if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['timeout', _('Connection timed out')] except (IMAP_IOError, IMAP4.error): if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['protocol', _('An IMAP protocol error occurred')] except (IOError, AttributeError, socket.error): if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['network', _('A network error occurred')] try: if conn: # Close the socket directly, in the hopes this will boot # any timed-out operations out of a hung state. conn.socket().shutdown(socket.SHUT_RDWR) conn.file.close() except (AttributeError, IOError, socket.error): pass if throw: raise throw(ev['error']) return WithaBool(False)
def open(self, conn_cls=None, throw=False): conn = self.conn conn_id = self._conn_id() if conn: try: with conn as c: if (conn_id == self.conn_id and self.timed(c.noop)[0] == 'OK'): # Make the timeout longer, so we don't drop things # on every hiccup and so downloads will be more # efficient (chunk size relates to timeout). self.timeout = self.TIMEOUT_LIVE return conn except self.CONN_ERRORS + (AttributeError, ): pass with self._lock: if self.conn == conn: self.conn = None conn.quit() # This facilitates testing, event should already exist in real life. if self.event: event = self.event else: event = Event(source=self, flags=Event.RUNNING, data={}) # Prepare the data section of our event, for keeping state. for d in ('mailbox_state', ): if d not in event.data: event.data[d] = {} ev = event.data['connection'] = { 'live': False, 'error': [False, _('Nothing is wrong')] } conn = None my_config = self.my_config mailboxes = my_config.mailbox.values() # If we are given a conn class, use that - this allows mocks for # testing. if not conn_cls: want_ssl = (my_config.protocol == 'imap_ssl') conn_cls = IMAP4_SSL if want_ssl else IMAP4 try: def mkconn(): return conn_cls(my_config.host, my_config.port) conn = self.timed(mkconn) conn.debug = ('imaplib' in self.session.config.sys.debug) and 4 or 0 ok, data = self.timed_imap(conn.capability) if ok: capabilities = set(' '.join(data).upper().split()) else: capabilities = set() #if 'STARTTLS' in capabilities and not want_ssl: # # FIXME: We need to send a STARTTLS and do a switcheroo where # the connection gets encrypted. try: ok, data = self.timed_imap(conn.login, my_config.username, my_config.password) except IMAP4.error: ok = False if not ok: ev['error'] = ['auth', _('Invalid username or password')] if throw: raise throw(event.data['conn_error']) return WithaBool(False) with self._lock: if self.conn is not None: raise IOError('Woah, we lost a race.') self.capabilities = capabilities if 'IDLE' in capabilities: self.conn = SharedImapConn( self.session, conn, idle_mailbox='INBOX', idle_callback=self._idle_callback) else: self.conn = SharedImapConn(self.session, conn) if self.event: self._log_status( _('Connected to IMAP server %s') % my_config.host) if 'imap' in self.session.config.sys.debug: self.session.ui.debug('CONNECTED %s' % self.conn) self.conn_id = conn_id ev['live'] = True return self.conn except TimedOut: if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['timeout', _('Connection timed out')] except (IMAP_IOError, IMAP4.error): if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['protocol', _('An IMAP protocol error occurred')] except (IOError, AttributeError, socket.error): if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['network', _('A network error occurred')] try: if conn: # Close the socket directly, in the hopes this will boot # any timed-out operations out of a hung state. conn.socket().shutdown(socket.SHUT_RDWR) conn.file.close() except (AttributeError, IOError, socket.error): pass if throw: raise throw(ev['error']) return WithaBool(False)
def open(self, conn_cls=None, throw=False): conn = self.conn if conn: try: with conn as c: if self.timed(c.noop)[0] == 'OK': return conn except self.CONN_ERRORS + (AttributeError, ): pass with self._lock: if self.conn == conn: self.conn = None conn.quit() conn = None my_config = self.my_config mailboxes = my_config.mailbox.values() # If we are given a conn class, use that - this allows mocks for # testing. if not conn_cls: want_ssl = (my_config.protocol == 'imap_ssl') conn_cls = IMAP4_SSL if want_ssl else IMAP4 # This also facilitates testing, should already exist in real life. if self.event: event = self.event else: event = Event(source=self, flags=Event.RUNNING, data={}) if 'conn_error' in event.data: del event.data['conn_error'] try: def mkconn(): return conn_cls(my_config.host, my_config.port) conn = self.timed(mkconn) conn.debug = ('imaplib' in self.session.config.sys.debug ) and 4 or 0 ok, data = self.timed_imap(conn.login, my_config.username, my_config.password) if not ok: event.data['conn_error'] = _('Bad username or password') if throw: raise throw(event.data['conn_error']) return WithaBool(False) ok, data = self.timed_imap(conn.capability) if ok: capabilities = set(' '.join(data).upper().split()) else: capabilities = set() with self._lock: if self.conn is not None: raise IOError('Woah, we lost a race.') self.capabilities = capabilities if 'IDLE' in capabilities: self.conn = SharedImapConn( self.session, conn, idle_mailbox='INBOX', idle_callback=self._idle_callback) else: self.conn = SharedImapConn(self.session, conn) # Prepare the data section of our event, for keeping state. for d in ('uidvalidity', 'uidnext'): if d not in event.data: event.data[d] = {} if self.event: self._log_status(_('Connected to IMAP server %s' ) % my_config.host) if 'imap' in self.session.config.sys.debug: self.session.ui.debug('CONNECTED %s' % self.conn) return self.conn except TimedOut: if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) event.data['conn_error'] = _('Connection timed out') except (IMAP_IOError, IMAP4.error): if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) event.data['conn_error'] = _('An IMAP protocol error occurred') except (IOError, AttributeError, socket.error): if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) event.data['conn_error'] = _('A network error occurred') try: if conn: # Close the socket directly, in the hopes this will boot # any timed-out operations out of a hung state. conn.socket().shutdown(socket.SHUT_RDWR) conn.file.close() except (AttributeError, IOError, socket.error): pass if throw: raise throw(event.data['conn_error']) return WithaBool(False)
def open(self, conn_cls=None, throw=False): conn = self.conn conn_id = self._conn_id() if conn: try: with conn as c: if conn_id == self.conn_id and self.timed(c.noop)[0] == "OK": return conn except self.CONN_ERRORS + (AttributeError,): pass with self._lock: if self.conn == conn: self.conn = None conn.quit() # This facilitates testing, event should already exist in real life. if self.event: event = self.event else: event = Event(source=self, flags=Event.RUNNING, data={}) # Prepare the data section of our event, for keeping state. for d in ("mailbox_state",): if d not in event.data: event.data[d] = {} ev = event.data["connection"] = {"live": False, "error": [False, _("Nothing is wrong")]} conn = None my_config = self.my_config mailboxes = my_config.mailbox.values() # If we are given a conn class, use that - this allows mocks for # testing. if not conn_cls: want_ssl = my_config.protocol == "imap_ssl" conn_cls = IMAP4_SSL if want_ssl else IMAP4 try: def mkconn(): return conn_cls(my_config.host, my_config.port) conn = self.timed(mkconn) conn.debug = ("imaplib" in self.session.config.sys.debug) and 4 or 0 try: ok, data = self.timed_imap(conn.login, my_config.username, my_config.password) except IMAP4.error: ok = False if not ok: ev["error"] = ["auth", _("Invalid username or password")] if throw: raise throw(event.data["conn_error"]) return WithaBool(False) ok, data = self.timed_imap(conn.capability) if ok: capabilities = set(" ".join(data).upper().split()) else: capabilities = set() with self._lock: if self.conn is not None: raise IOError("Woah, we lost a race.") self.capabilities = capabilities if "IDLE" in capabilities: self.conn = SharedImapConn( self.session, conn, idle_mailbox="INBOX", idle_callback=self._idle_callback ) else: self.conn = SharedImapConn(self.session, conn) if self.event: self._log_status(_("Connected to IMAP server %s") % my_config.host) if "imap" in self.session.config.sys.debug: self.session.ui.debug("CONNECTED %s" % self.conn) self.conn_id = conn_id ev["live"] = True return self.conn except TimedOut: if "imap" in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev["error"] = ["timeout", _("Connection timed out")] except (IMAP_IOError, IMAP4.error): if "imap" in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev["error"] = ["protocol", _("An IMAP protocol error occurred")] except (IOError, AttributeError, socket.error): if "imap" in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev["error"] = ["network", _("A network error occurred")] try: if conn: # Close the socket directly, in the hopes this will boot # any timed-out operations out of a hung state. conn.socket().shutdown(socket.SHUT_RDWR) conn.file.close() except (AttributeError, IOError, socket.error): pass if throw: raise throw(ev["error"]) return WithaBool(False)
def _unlocked_open(self, conn_cls=None): # # When opening an IMAP connection, we need to do a few things: # 1. Connect, log in # 2. Check the capabilities of the remote server # 3. If there is IDLE support, subscribe to all the paths we # are currently watching. my_config = self.my_config mailboxes = my_config.mailbox.values() if self.conn: try: with self.conn as conn: if self._timed(conn.noop)[0] == 'OK': return True except self.CONN_ERRORS: self.conn.quit() conn = self.conn = None # If we are given a conn class, use that - this allows mocks for # testing. if not conn_cls: want_ssl = (my_config.protocol == 'imap_ssl') conn_cls = IMAP4_SSL if want_ssl else IMAP4 # This also facilitates testing, should already exist in real life. if self.event: event = self.event else: event = Event(source=self, flags=Event.RUNNING, data={}) if 'conn_error' in event.data: del event.data['conn_error'] try: def mkconn(): return conn_cls(my_config.host, my_config.port) conn = self._timed(mkconn) ok, data = _parse_imap(self._timed(conn.login, my_config.username, my_config.password)) if not ok: event.data['conn_error'] = _('Bad username or password') return False ok, data = _parse_imap(self._timed(conn.capability)) if ok: self.capabilities = set(' '.join(data).upper().split()) else: self.capabilities = set() if 'IDLE' in self.capabilities: self.conn = SharedImapConn(conn, idle_mailbox='INBOX', idle_callback=self._idle_callback) else: self.conn = SharedImapConn(conn) # Prepare the data section of our event, for keeping state. for d in ('uidvalidity', 'uidnext'): if d not in self.event.data: event.data[d] = {} self._log_status(_('Connected to IMAP server %s' ) % my_config.host) return True except TimedOut: event.data['conn_error'] = _('Connection timed out') except (socket.error, AttributeError): event.data['conn_error'] = _('A network error occurred') except IMAP4.error: event.data['conn_error'] = _('An IMAP error occurred') try: if conn: # Close the socket directly, in the hopes this will boot # any timed-out operations out of a hung state. conn.socket().shutdown() conn.file.close() except (AttributeError, IOError, socket.error): pass return False return True
def open(self, conn_cls=None, throw=False): conn = self.conn conn_id = self._conn_id() if conn: try: with conn as c: now = time.time() if (conn_id == self.conn_id and (now < self.last_op + 120 or self.timed(c.noop)[0] == 'OK')): # Make the timeout longer, so we don't drop things # on every hiccup and so downloads will be more # efficient (chunk size relates to timeout). self.timeout = self.TIMEOUT_LIVE if now >= self.last_op + 120: self.last_op = now return conn except self.CONN_ERRORS + (AttributeError, ): pass with self._lock: if self.conn == conn: self.conn = None conn.quit() # This facilitates testing, event should already exist in real life. if self.event: event = self.event else: event = Event(source=self, flags=Event.RUNNING, data={}) # Prepare the data section of our event, for keeping state. for d in ('mailbox_state',): if d not in event.data: event.data[d] = {} ev = event.data['connection'] = { 'live': False, 'error': [False, _('Nothing is wrong')] } conn = None my_config = self.my_config mailboxes = my_config.mailbox.values() # If we are given a conn class, use that - this allows mocks for # testing. if not conn_cls: req_stls = (my_config.protocol == 'imap_tls') want_ssl = (my_config.protocol == 'imap_ssl') conn_cls = IMAP4_SSL if want_ssl else IMAP4 else: req_stls = want_ssl = False try: def mkconn(): with ConnBroker.context(need=[ConnBroker.OUTGOING_IMAP]): return conn_cls(my_config.host, my_config.port) conn = self.timed(mkconn) conn.debug = ('imaplib' in self.session.config.sys.debug ) and 4 or 0 ok, data = self.timed_imap(conn.capability) if ok: capabilities = set(' '.join(data).upper().split()) else: capabilities = set() if req_stls or ('STARTTLS' in capabilities and not want_ssl): try: ok, data = self.timed_imap(conn.starttls) if ok: # Fetch capabilities again after STARTTLS ok, data = self.timed_imap(conn.capability) capabilities = set(' '.join(data).upper().split()) except (IMAP4.error, IOError, socket.error): ok = False if not ok: ev['error'] = ['protocol', _('Failed to STARTTLS')] if throw: raise throw(ev['error'][1]) return WithaBool(False) try: username = my_config.get('username', '').encode('utf-8') password = my_config.get('password', '').encode('utf-8') ok, data = self.timed_imap(conn.login, username, password) except (IMAP4.error, UnicodeDecodeError): ok = False if not ok: ev['error'] = ['auth', _('Invalid username or password')] if throw: raise throw(ev['error'][1]) return WithaBool(False) with self._lock: if self.conn is not None: raise IOError('Woah, we lost a race.') self.capabilities = capabilities if 'IDLE' in capabilities: self.conn = SharedImapConn( self.session, conn, idle_mailbox='INBOX', idle_callback=self._idle_callback) else: self.conn = SharedImapConn(self.session, conn) if 'NAMESPACE' in capabilities: ok, data = self.timed_imap(conn.namespace) if ok: prv, oth, shr = data self.namespaces = { 'private': prv if (prv != 'NIL') else [], 'others': oth if (oth != 'NIL') else [], 'shared': shr if (shr != 'NIL') else [] } if self.event: self._log_status(_('Connected to IMAP server %s' ) % my_config.host) if 'imap' in self.session.config.sys.debug: self.session.ui.debug('CONNECTED %s' % self.conn) self.session.ui.debug('CAPABILITIES %s' % self.capabilities) self.session.ui.debug('NAMESPACES %s' % self.namespaces) self.conn_id = conn_id ev['live'] = True return self.conn except TimedOut: if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['timeout', _('Connection timed out')] except (IMAP_IOError, IMAP4.error): if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['protocol', _('An IMAP protocol error occurred')] except (IOError, AttributeError, socket.error): if 'imap' in self.session.config.sys.debug: self.session.ui.debug(traceback.format_exc()) ev['error'] = ['network', _('A network error occurred')] try: if conn: # Close the socket directly, in the hopes this will boot # any timed-out operations out of a hung state. conn.socket().shutdown(socket.SHUT_RDWR) conn.file.close() except (AttributeError, IOError, socket.error): pass if throw: raise throw(ev['error']) return WithaBool(False)