Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
 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)
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
0
    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)
Ejemplo n.º 12
0
    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)
Ejemplo n.º 13
0
    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
Ejemplo n.º 14
0
    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)