def _connect(self): with self._lock: if self._pop3: try: self._pop3.noop() return except poplib.error_proto: self._pop3 = None with ConnBroker.context(need=[ConnBroker.OUTGOING_POP3]): if self.conn_cls: self._pop3 = self.conn_cls(self.host, self.port or 110, timeout=120) self.secure = self.use_ssl elif self.use_ssl: self._pop3 = wrappable_POP3_SSL(self.host, self.port or 995, timeout=120) self.secure = True else: self._pop3 = poplib.POP3(self.host, self.port or 110, timeout=120) self.secure = False if hasattr(self._pop3, 'sock'): self._pop3.sock.settimeout(120) if self.debug: self._pop3.set_debuglevel(self.debug) self._keys = None try: if self.auth_type.lower() == 'oauth2': from mailpile.plugins.oauth import OAuth2 token_info = OAuth2.GetFreshTokenInfo( self.session, self.user) if self.user and token_info and token_info.access_token: raise AccessError("FIXME: Do OAUTH2 Auth!") else: raise AccessError() else: self._pop3.user(self.user) self._pop3.pass_(self.password.encode('utf-8')) except poplib.error_proto: raise AccessError()
def _connect_imap(session, settings, event, conn_cls=None, timeout=30, throw=False, logged_in_cb=None, source=None): def timed(*args, **kwargs): if source is not None: kwargs['unique_thread'] = 'imap/%s' % (source.my_config._key, ) return RunTimed(timeout, *args, **kwargs) def timed_imap(*args, **kwargs): if source is not None: kwargs['unique_thread'] = 'imap/%s' % (source.my_config._key, ) return _parse_imap(RunTimed(timeout, *args, **kwargs)) conn = None try: # 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')] } # If we are given a conn class, use that - this allows mocks for # testing. if not conn_cls: req_stls = (settings.get('protocol') == 'imap_tls') want_ssl = (settings.get('protocol') == 'imap_ssl') conn_cls = IMAP4_SSL if want_ssl else IMAP4 else: req_stls = want_ssl = False def mkconn(): if want_ssl: need = [ConnBroker.OUTGOING_IMAPS] else: need = [ConnBroker.OUTGOING_IMAP] with ConnBroker.context(need=need): return conn_cls(settings.get('host'), int(settings.get('port'))) conn = timed(mkconn) if hasattr(conn, 'sock'): conn.sock.settimeout(120) conn.debug = ('imaplib' in session.config.sys.debug) and 4 or 0 ok, data = 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 = timed_imap(conn.starttls) if ok: # Fetch capabilities again after STARTTLS ok, data = timed_imap(conn.capability) capabilities = set(' '.join(data).upper().split()) # Update the protocol to avoid getting downgraded later if settings.get('protocol', '') != 'imap_ssl': settings['protocol'] = 'imap_tls' except (IMAP4.error, IOError, socket.error): ok = False if not ok: ev['error'] = [ 'tls', _('Failed to make a secure TLS connection'), '%s:%s' % (settings.get('host'), settings.get('port')) ] if throw: raise throw(ev['error'][1]) return WithaBool(False) username = password = "" try: error_type = 'auth' error_msg = _('Invalid username or password') username = settings.get('username', '').encode('utf-8') password = IndirectPassword(session.config, settings.get('password', '')).encode('utf-8') if (settings.get('auth_type', '').lower() == 'oauth2' and 'AUTH=XOAUTH2' in capabilities): error_type = 'oauth2' error_msg = _('Access denied by mail server') token_info = OAuth2.GetFreshTokenInfo(session, username) if not (username and token_info and token_info.access_token): raise ValueError("Missing configuration") ok, data = timed_imap( conn.authenticate, 'XOAUTH2', lambda challenge: OAuth2.XOAuth2Response( username, token_info)) if not ok: token_info.access_token = '' else: ok, data = timed_imap(conn.login, username, password) except (IMAP4.error, UnicodeDecodeError, ValueError): ok, data = False, None if not ok: auth_summary = '' if source is not None: auth_summary = source._summarize_auth() ev['error'] = [error_type, error_msg, username, auth_summary] if throw: raise throw(ev['error'][1]) return WithaBool(False) if logged_in_cb is not None: logged_in_cb(conn, ev, capabilities) return conn except TimedOut: if 'imap' in session.config.sys.debug: session.ui.debug('%s' % traceback.format_exc()) ev['error'] = ['timeout', _('Connection timed out')] except (ssl.CertificateError, ssl.SSLError): if 'imap' in session.config.sys.debug: session.ui.debug('%s' % traceback.format_exc()) ev['error'] = [ 'tls', _('Failed to make a secure TLS connection'), '%s:%s' % (settings.get('host'), settings.get('port')) ] except (IMAP_IOError, IMAP4.error): if 'imap' in session.config.sys.debug: session.ui.debug('%s' % traceback.format_exc()) ev['error'] = ['protocol', _('An IMAP protocol error occurred')] except (IOError, AttributeError, socket.error): if 'imap' in session.config.sys.debug: session.ui.debug('%s' % 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 None
def sm_startup(): try: server = sm_connect_server() if not smtp_ssl: # We always try to enable TLS, even if the user just # requested plain-text smtp. But we only throw errors # if the user asked for encryption. try: server.starttls() server.ehlo_or_helo_if_needed() except: if proto == 'smtptls': raise else: server = sm_connect_server() except (ssl.CertificateError, ssl.SSLError): fail(_('Failed to make a secure TLS connection'), events, details={ 'tls_error': True, 'server': '%s:%d' % (host, port)}, exception=InsecureSmtpError) serverbox[0] = server if user: try: if auth_type.lower() == 'oauth2': from mailpile.plugins.oauth import OAuth2 tok_info = OAuth2.GetFreshTokenInfo(session, user) if not (user and tok_info and tok_info.access_token): fail(_('Access denied by mail server'), events, details={'oauth_error': True, 'username': user}) authstr = (OAuth2.XOAuth2Response(user, tok_info) ).encode('base64').replace('\n', '') server.docmd('AUTH', 'XOAUTH2 ' + authstr) else: server.login(user.encode('utf-8'), (pwd or '').encode('utf-8')) except UnicodeDecodeError: fail(_('Bad character in username or password'), events, details={'authentication_error': True, 'username': user}) except smtplib.SMTPAuthenticationError: fail(_('Invalid username or password'), events, details={'authentication_error': True, 'username': user}) except smtplib.SMTPException: # If the server does not support authentication, assume # it's passwordless and try to carry one anyway. pass smtp_do_or_die(_('Sender rejected by SMTP server'), events, server.mail, frm) for rcpt in to: rc, msg = server.rcpt(rcpt) if (rc == SMTORP_HASHCASH_RCODE and msg.startswith(SMTORP_HASHCASH_PREFIX)): rc, msg = server.rcpt(SMTorP_HashCash(rcpt, msg)) if rc != 250: fail(_('Server rejected recipient: %s') % rcpt, events) rcode, rmsg = server.docmd('DATA') if rcode != 354: fail(_('Server rejected DATA: %s %s') % (rcode, rmsg))