def execCommand(self, callable, *args, **keywds):
        try:
            result = zanshin.util.blockUntil(callable, *args, **keywds)
            return (1, result)

        except Utility.CertificateVerificationError, err:
            assert err.args[1] == 'certificate verify failed'

            # Reason why verification failed is stored in err.args[0], see
            # codes at http://www.openssl.org/docs/apps/verify.html#DIAGNOSTICS
            try:
                result = (2, None)
                # Send the message to destroy the progress dialog first. This needs
                # to be done in this order on Linux because otherwise killing
                # the progress dialog will also kill the SSL error dialog.
                # Weird, huh? Welcome to the world of wx...
                callMethodInUIThread(self.callback, result)
                if err.args[0] in ssl.unknown_issuer:
                    d = ssl.askTrustServerCertificate(
                        err.host, err.untrustedCertificates[0], self.reconnect)
                else:
                    d = ssl.askIgnoreSSLError(err.host,
                                              err.untrustedCertificates[0],
                                              err.args[0], self.reconnect)

                waitForDeferred(d)

                return result
            except Exception, e:
                # There is a bug in the M2Crypto code which needs
                # to be fixed.
                #log.exception('This should not happen')
                return (0, (CANT_CONNECT, _(u"SSL error.")))
示例#2
0
    def execCommand(self, callable, *args, **keywds):
        try:
            result =  zanshin.util.blockUntil(callable, *args, **keywds)
            return (1, result)

        except Utility.CertificateVerificationError, err:
            assert err.args[1] == 'certificate verify failed'

            # Reason why verification failed is stored in err.args[0], see
            # codes at http://www.openssl.org/docs/apps/verify.html#DIAGNOSTICS
            try:
                result = (2, None)
                # Send the message to destroy the progress dialog first. This needs
                # to be done in this order on Linux because otherwise killing
                # the progress dialog will also kill the SSL error dialog.
                # Weird, huh? Welcome to the world of wx...
                callMethodInUIThread(self.callback, result)
                if err.args[0] in ssl.unknown_issuer:
                    d = ssl.askTrustServerCertificate(err.host,
                                                      err.untrustedCertificates[0],
                                                      self.reconnect)
                else:
                    d = ssl.askIgnoreSSLError(err.host,
                                              err.untrustedCertificates[0],
                                              err.args[0],
                                              self.reconnect)

                waitForDeferred(d)

                return result
            except Exception, e:
                # There is a bug in the M2Crypto code which needs
                # to be fixed.
                #log.exception('This should not happen')
                return (0, (CANT_CONNECT, _(u"SSL error.")))
示例#3
0
    def blockUntil(self, callable, *args, **keywds):
        # Since there can be several errors in a connection, we must keep 
        # trying until we either get a successful connection or the user 
        # decides to cancel/disconnect, or there is an error we don't know 
        # how to deal with.
        while True:
            try:
                return zanshin.util.blockUntil(callable, *args, **keywds)
            except Utility.CertificateVerificationError, err:
                self._reconnect = False

                assert err.args[1] == 'certificate verify failed'
    
                # Reason why verification failed is stored in err.args[0], see
                # codes at http://www.openssl.org/docs/apps/verify.html#DIAGNOSTICS
    
                retry = lambda: True
    
                if err.args[0] in ssl.unknown_issuer:
                    d = ssl.askTrustServerCertificate(
                        err.host,
                        err.untrustedCertificates[0], 
                        retry)
                else:
                    d = ssl.askIgnoreSSLError(
                        err.host,
                        err.untrustedCertificates[0], 
                        err.args[0], 
                        retry)

                if not waitForDeferred(d):
                    raise ActivityAborted(_(u"Cancelled by user"))
                                            
            except M2Crypto.SSL.Checker.WrongHost, err:
                self._reconnect = False

                retry = lambda: True
        
                d = ssl.askIgnoreSSLError(
                    err.expectedHost,
                    err.pem, 
                    messages.SSL_HOST_MISMATCH % {'actualHost': err.actualHost},
                    retry)

                if not waitForDeferred(d):
                    raise ActivityAborted(_(u"Cancelled by user"))
    def blockUntil(self, callable, *args, **keywds):
        # Since there can be several errors in a connection, we must keep
        # trying until we either get a successful connection or the user
        # decides to cancel/disconnect, or there is an error we don't know
        # how to deal with.
        while True:
            try:
                return zanshin.util.blockUntil(callable, *args, **keywds)
            except Utility.CertificateVerificationError, err:
                self._reconnect = False

                assert err.args[1] == 'certificate verify failed'

                # Reason why verification failed is stored in err.args[0], see
                # codes at http://www.openssl.org/docs/apps/verify.html#DIAGNOSTICS

                retry = lambda: True

                if err.args[0] in ssl.unknown_issuer:
                    d = ssl.askTrustServerCertificate(
                        err.host, err.untrustedCertificates[0], retry)
                else:
                    d = ssl.askIgnoreSSLError(err.host,
                                              err.untrustedCertificates[0],
                                              err.args[0], retry)

                if not waitForDeferred(d):
                    raise ActivityAborted(_(u"Cancelled by user"))

            except M2Crypto.SSL.Checker.WrongHost, err:
                self._reconnect = False

                retry = lambda: True

                d = ssl.askIgnoreSSLError(
                    err.expectedHost, err.pem, messages.SSL_HOST_MISMATCH %
                    {'actualHost': err.actualHost}, retry)

                if not waitForDeferred(d):
                    raise ActivityAborted(_(u"Cancelled by user"))
示例#5
0
    def displayedRecoverableSSLErrorDialog(self,
                                           err,
                                           mailMessage=None,
                                           dryRun=False):
        if __debug__:
            trace("displayedRecoverableSSLErrorDialog")

        # The isOnline check is performed in the Twisted thread
        # so pass in a view.
        if not dryRun and (self.shuttingDown or not Globals.mailService.isOnline(self.view) or \
           self.cancel):
            return self._resetClient()

        if isinstance(err, Utility.CertificateVerificationError):
            assert err.args[1] == 'certificate verify failed'
            # Reason why verification failed is stored in err.args[0], see
            # codes at http://www.openssl.org/docs/apps/verify.html#DIAGNOSTICS

            # Post an asynchronous event to the main thread where
            # we ask the user if they would like to trust this
            # certificate. The main thread will then initiate a retry
            # when the new certificate has been added.
            try:
                if not dryRun:
                    if err.args[0] in ssl.unknown_issuer:
                        d = ssl.askTrustServerCertificate(
                            err.host, err.untrustedCertificates[0],
                            self.reconnect)
                    else:
                        d = ssl.askIgnoreSSLError(err.host,
                                                  err.untrustedCertificates[0],
                                                  err.args[0], self.reconnect)

                    d.addCallback(lambda dummy: True)

                return True
            except Exception, e:
                # This code should never be reached.
                log.exception(
                    'Error raised in SSL Layer which requires investigation.')
                return False
示例#6
0
    def displayedRecoverableSSLErrorDialog(self, err, mailMessage=None,
                                           dryRun=False):
        if __debug__:
            trace("displayedRecoverableSSLErrorDialog")

        # The isOnline check is performed in the Twisted thread
        # so pass in a view.
        if not dryRun and (self.shuttingDown or not Globals.mailService.isOnline(self.view) or \
           self.cancel):
            return self._resetClient()

        if isinstance(err, Utility.CertificateVerificationError):
            assert err.args[1] == 'certificate verify failed'
            # Reason why verification failed is stored in err.args[0], see
            # codes at http://www.openssl.org/docs/apps/verify.html#DIAGNOSTICS

            # Post an asynchronous event to the main thread where
            # we ask the user if they would like to trust this
            # certificate. The main thread will then initiate a retry
            # when the new certificate has been added.
            try:
                if not dryRun:
                    if err.args[0] in ssl.unknown_issuer:
                        d = ssl.askTrustServerCertificate(err.host,
                                                          err.untrustedCertificates[0],
                                                          self.reconnect)
                    else:
                        d = ssl.askIgnoreSSLError(err.host,
                                                  err.untrustedCertificates[0],
                                                  err.args[0],
                                                  self.reconnect)
                        
                    d.addCallback(lambda dummy: True)
                    
                return True
            except Exception, e:
                # This code should never be reached.
                log.exception('Error raised in SSL Layer which requires investigation.')
                return False
示例#7
0
                                                  err.args[0], self.reconnect)

                    d.addCallback(lambda dummy: True)

                return True
            except Exception, e:
                # This code should never be reached.
                log.exception(
                    'Error raised in SSL Layer which requires investigation.')
                return False

        elif isinstance(err, WrongHost):
            if not dryRun:
                d = ssl.askIgnoreSSLError(err.expectedHost,
                                          err.pem,
                                          messages.SSL_HOST_MISMATCH % \
                                            {'actualHost': err.actualHost},
                                          self.reconnect)

                d.addCallback(lambda dummy: True)

            return True

        return False

    def cancelLastRequest(self):
        if __debug__:
            trace("cancelLastRequest")

        # This feature is still experimental
示例#8
0
        if isinstance(err, WrongHost):
            # Post an asynchronous event to the main thread where
            # we ask the user if they would like to continue even though
            # the certificate identifies a different host.

            if self.callback:
                # Send the message to destroy the progress dialog first. This needs
                # to be done in this order on Linux because otherwise killing
                # the progress dialog will also kill the SSL error dialog.
                # Weird, huh? Welcome to the world of wx...
                callMethodInUIThread(self.callback, (2, None))

            d = ssl.askIgnoreSSLError(err.expectedHost,
                                      err.pem,
                                      messages.SSL_HOST_MISMATCH % \
                                        {'actualHost': err.actualHost},
                                      self.reconnect)

            d.addCallback(lambda dummy: True)

            return self._actionCompleted()

        errorText = unicode(err.__str__(), 'utf8', 'ignore')

        if self.callback:
            callMethodInUIThread(self.callback, (0, errorText))
        else:
            if isinstance(err, errors.IMAPTimeoutException):
                alertMailError(constants.MAIL_PROTOCOL_TIMEOUT_ERROR, self.account,
                               {'hostName': self.account.host},
示例#9
0
class AbstractDownloadClient(object):
    """ Base class for Chandler download transports (IMAP, POP, etc.)
        Encapsulates logic for interactions between Twisted protocols (POP, IMAP)
        and Chandler protocol clients"""

    #Subclasses overide these constants
    accountType = AccountBase
    clientType = "AbstractDownloadClient"
    factoryType = AbstractDownloadClientFactory
    defaultPort = 0

    def __init__(self, view, account, mailWorker):
        """
        @param view: An Instance of C{RepositoryView}
        @type view: C{RepositoryView}
        @param account: An Instance of C{DownloadAccountBase}
        @type account: C{DownloadAccount}
        @param mailWorker: An Instance of C{MailWorker}
        @type mailWorker: C{MailWorker}
        @return: C{None}
        """
        assert isinstance(account, self.accountType)
        assert isinstance(view, RepositoryView)
        assert isinstance(mailWorker, mailworker.MailWorker)

        self.view = view
        self.mailWorker = mailWorker

        #These values exist for life of client
        self.accountUUID = account.itsUUID
        self.account = None
        self.shuttingDown = False

        # Stores the total number of messages
        # downloaded during the life of the
        # client. In the IMAP example this
        # value would hold the total number
        # of messages downloaded for all
        # folders where as DownloadVars.totalDownloaded
        # would hold the total number of messages download
        # for the given folder.
        self.totalDownloaded = 0

        # These values are reassigned on each
        # connection to the server
        self.performingAction = False
        self.waitingOnCommit = False
        self.waitCallback = None
        self.testing = False
        self.logIn = True
        self.callback = None
        self.statusMessages = False
        self.factory = None
        self.proto = None
        self.reconnect = None
        self.errorHandled = False

        # Used to Log client server protocol exchanges.
        # On error if constants.DEBUG_CLIENT_SERVER = 1
        # the catchErrors method will print the values
        # in the buffer to the stdout.
        self.clientServerBuffer = None

        # Cancel is experimental and used
        # with the account testing dialog.
        # More work still needs to be done
        # for it to be useful for all protocol
        # based operations.
        self.cancel = False

        #The internal class method to
        #call after initial connection
        #to the server and login / SSL / TLS
        # is completed.
        self.cb = None

        self.vars = None

    def getMail(self):
        """Retrieves mail from a download protocol (POP, IMAP)"""
        if __debug__:
            trace("getMail")

        # Tells whether to print status messages
        self.statusMessages = True

        # Tell what method to call if the SSL acceptance
        # dialog needs to be displayed
        self.reconnect = self.getMail

        # The first callback after login / TLS /SSL
        # complete
        self.cb = self._getMail

        # Move code execution path from current thread
        # to Reactor Asynch thread
        reactor.callFromThread(self._connectToServer)

    def testAccountSettings(self, callback, reconnect, logIn=True):
        """Tests the account settings for a download protocol (POP, IMAP).
           Raises an error if unable to establish or communicate properly
           with the a server.
        """
        if __debug__:
            trace("testAccountSettings")

        # The isOnline check is performed on the Main Thread
        # so no need to pass in a view.
        if not Globals.mailService.isOnline():
            return

        assert (callback is not None)
        assert (reconnect is not None)
        assert (isinstance(logIn, bool))

        # Tells whether to print status messages
        self.statusMessages = False

        # Tell what method to call on reconnect
        # when a SSL dialog is displayed.
        # When the dialog is shown the
        # protocol code terminates the
        # connection and calls reconnect
        # if the cert has been accepted
        self.reconnect = reconnect

        # The method to call in the UI Thread
        # when the testing is complete.
        # This method is called for both success and failure.
        self.callback = callback

        # Preview timeframe addition that
        # specifies whether the protocol should
        # login the user as part of the account
        # testing. A value of True will log
        # the user in as part of the test.
        self.logIn = logIn

        # The first callback after login / TLS /SSL
        # complete
        self.cb = self._accountTestingComplete

        # Flag indicating we are in test mode
        self.testing = True

        # Move code execution path from current thread
        # to Reactor Asynch thread
        reactor.callFromThread(self._connectToServer)

    def _connectToServer(self):
        if __debug__:
            trace("_connectToServer")

        if self.cancel:
            return self._resetClient()

        self.view.refresh()

        #Overidden method
        self._getAccount()

        # The isOnline check is performed in the Twisted thread
        # so pass in a view.
        if not Globals.mailService.isOnline(self.view):
            if self.statusMessages:
                msg = constants.MAIL_PROTOCOL_OFFLINE % \
                              {"accountName": self.account.displayName}

                setStatusMessage(msg)
            return

        if self.performingAction:
            if __debug__:
                trace("%s is currently in use request ignored" %
                      self.clientType)
            return

        self.performingAction = True

        if self.statusMessages:
            msg = constants.MAIL_PROTOCOL_CONNECTION \
                          % {"accountName": self.account.displayName,
                             "serverDNSName": self.account.host}

            setStatusMessage(msg)

        self.factory = self.factoryType(self)

        if self.testing:
            # If in testing mode then do not want to retry connection or
            # wait a long period for a timeout
            self.factory.retries = 0
            self.factory.timeout = constants.TESTING_TIMEOUT

        if self.account.connectionSecurity == 'SSL':
            ssl.connectSSL(self.account.host, self.account.port, self.factory,
                           self.view)
        else:
            ssl.connectTCP(self.account.host, self.account.port, self.factory,
                           self.view)

    def calculateCommitNumber(self):
        # Low commit numbers are used
        # till the UI has enough messages to
        # extend pass the scroll bars.
        # At that point the commit sizes
        # are increased gradually until
        # reaching the ideal number of
        # commits vs. downloaded messages
        # from a performance standpoint.
        total = self.totalDownloaded

        if total < 24:
            return 6
        elif total >= 24 and total < 100:
            return 20
        elif total >= 100 and total < 300:
            return 100
        elif total >= 300 and total < 600:
            return 200

        return constants.MAX_COMMIT

    def catchErrors(self, err):
        """
        This method captures all errors thrown while in the Twisted Reactor Thread as well
        as errors raised by non-Twisted code while in the Twisted Reactor Thread.
        catchErrors will print a stacktrace of C{failure.Failure} objects to the chandler.log.
        catchErrors also handles c{Exception}s but will not log the stacktrace to the chandler.log
        since this method is out of the scope of the original c{Exception}. The caller must log 
        its c{Exception} via the logging.exception method.

        @param err: The error thrown
        @type err: C{failure.Failure} or c{Exception}

        @return: C{None}
        """
        if __debug__:
            trace("catchErrors")

        self.waitingOnCommit = False

        if self.waitCallback is not None:
            try:
                self.waitCallback.cancel()
            except error.AlreadyCalled:
                pass

            self.waitCallback = None

        if __debug__:
            if constants.DEBUG_CLIENT_SERVER == 1 and \
                self.clientServerBuffer:
                # Prints the last four client server exchanges to
                # the stdout for debugging.
                print self.clientServerBuffer

            if constants.DEBUG_CLIENT_SERVER == 2:
                try:
                    raise err
                except Exception, err:
                    #Capture the error to the logger
                    logging.exception(err)

        if self.vars:
            # If self.vars is not None then the error
            # was raised during an Incoming Mail download.
            # In this case we want to notify the Mail Worker
            # so it can perform any cleanup that is needed.
            self.mailWorker.queueRequest(
                (mailworker.ERROR_REQUEST, self, self.accountUUID))

        # Flag that tells the connection factory
        # that the error has been handled
        self.errorHandled = True

        # In this case don't try to clean up the transport connection
        # but do reset the client variables
        # The isOnline check is performed in the Twisted thread so
        # pass in a view.
        if self.shuttingDown or not Globals.mailService.isOnline(self.view) or \
           self.factory is None:
            self._resetClient()
            return

        # If we cancelled the request then gracefully disconnect from
        # the server and reset the client variables but do not display
        # the error.
        if self.cancel:
            return self._actionCompleted()

        if isinstance(err, failure.Failure):
            if err.check(error.ConnectionDone):
                if self.factory.retries < self.factory.sendFinished <= 0:
                    #The error processing for lost connections is in the Factory
                    #class so return here and let the Factory handle the reconnection logic.
                    return

                #set the value of the error to something more meaningful than
                #'Connection closed cleanly.'
                err.value = self.factory.exception(
                    constants.MAIL_PROTOCOL_CONNECTION_ERROR)

            err = err.value

        if self.statusMessages:
            # Reset the status bar since the error will be displayed
            # in a dialog or handled by a callback method.
            setStatusMessage(u"")

        if isinstance(err, Utility.CertificateVerificationError):
            assert err.args[1] == 'certificate verify failed'

            # Reason why verification failed is stored in err.args[0], see
            # codes at http://www.openssl.org/docs/apps/verify.html#DIAGNOSTICS

            # Post an asynchronous event to the main thread where
            # we ask the user if they would like to trust this
            # certificate. The main thread will then initiate a retry
            # when the new certificate has been added.
            try:
                if self.callback:
                    # Send the message to destroy the progress dialog first. This needs
                    # to be done in this order on Linux because otherwise killing
                    # the progress dialog will also kill the SSL error dialog.
                    # Weird, huh? Welcome to the world of wx...
                    callMethodInUIThread(self.callback, (2, None))

                if err.args[0] in ssl.unknown_issuer:
                    d = ssl.askTrustServerCertificate(
                        err.host, err.untrustedCertificates[0], self.reconnect)
                else:
                    d = ssl.askIgnoreSSLError(err.host,
                                              err.untrustedCertificates[0],
                                              err.args[0], self.reconnect)

                d.addCallback(lambda dummy: True)

            except Exception, e:
                # This code should never be reached, and if it were, the _ would raise!
                log.exception(
                    'Error raised in SSL Layer which requires investigation.')
                callMethodInUIThread(self.callback, (0, _(u"SSL error.")))

            return self._actionCompleted()