예제 #1
0
    def _route_msg(self):
        """
        Sends the msg using the ESMTPSenderFactory.
        """
        log.msg("Connecting to SMTP server %s:%s" % (self._host, self._port))
        msg = self._msg.as_string(False)

        # we construct a defer to pass to the ESMTPSenderFactory
        d = defer.Deferred()
        d.addCallbacks(self.sendSuccess, self.sendError)
        # we don't pass an ssl context factory to the ESMTPSenderFactory
        # because ssl will be handled by reactor.connectSSL() below.
        factory = smtp.ESMTPSenderFactory(
            "",  # username is blank because server does not use auth.
            "",  # password is blank because server does not use auth.
            self._fromAddress.addrstr,
            self._user.dest.addrstr,
            StringIO(msg),
            d,
            heloFallback=True,
            requireAuthentication=False,
            requireTransportSecurity=True)
        factory.domain = __version__
        signal(proto.SMTP_SEND_MESSAGE_START, self._user.dest.addrstr)
        reactor.connectSSL(self._host,
                           self._port,
                           factory,
                           contextFactory=SSLContextFactory(
                               self._cert, self._key))
예제 #2
0
파일: mailbox.py 프로젝트: pmaia/leap_mail
 def __cb_signal_unread_to_ui(self, unseen):
     """
     Send the unread signal to UI.
     :param unseen: number of unseen messages.
     :type unseen: int
     """
     leap_events.signal(IMAP_UNREAD_MAIL, str(unseen))
예제 #3
0
파일: fetch.py 프로젝트: pmaia/leap_mail
    def _decrypt_doc(self, doc):
        """
        Decrypt the contents of a document.

        :param doc: A document containing an encrypted message.
        :type doc: SoledadDocument

        :return: A tuple containing the document and the decrypted message.
        :rtype: (SoledadDocument, str)
        """
        log.msg('decrypting msg')
        success = False

        try:
            decrdata = self._keymanager.decrypt(
                doc.content[ENC_JSON_KEY],
                self._pkey)
            success = True
        except Exception as exc:
            # XXX move this to errback !!!
            logger.error("Error while decrypting msg: %r" % (exc,))
            decrdata = ""
        leap_events.signal(IMAP_MSG_DECRYPTED, "1" if success else "0")

        data = self._process_decrypted_doc((doc, decrdata))
        return (doc, data)
예제 #4
0
    def _route_msg(self):
        """
        Sends the msg using the ESMTPSenderFactory.
        """
        log.msg("Connecting to SMTP server %s:%s" % (self._host, self._port))
        msg = self._msg.as_string(False)

        # we construct a defer to pass to the ESMTPSenderFactory
        d = defer.Deferred()
        d.addCallbacks(self.sendSuccess, self.sendError)
        # we don't pass an ssl context factory to the ESMTPSenderFactory
        # because ssl will be handled by reactor.connectSSL() below.
        factory = smtp.ESMTPSenderFactory(
            "",  # username is blank because server does not use auth.
            "",  # password is blank because server does not use auth.
            self._fromAddress.addrstr,
            self._user.dest.addrstr,
            StringIO(msg),
            d,
            heloFallback=True,
            requireAuthentication=False,
            requireTransportSecurity=True)
        factory.domain = __version__
        signal(proto.SMTP_SEND_MESSAGE_START, self._user.dest.addrstr)
        reactor.connectSSL(
            self._host, self._port, factory,
            contextFactory=SSLContextFactory(self._cert, self._key))
예제 #5
0
    def send_key(self, ktype):
        """
        Send user's key of type C{ktype} to provider.

        Public key bound to user's is sent to provider, which will sign it and
        replace any prior keys for the same address in its database.

        If C{send_private} is True, then the private key is encrypted with
        C{password} and sent to server in the same request, together with a
        hash string of user's address and password. The encrypted private key
        will be saved in the server in a way it is publicly retrievable
        through the hash string.

        :param ktype: The type of the key.
        :type ktype: KeyType

        @raise KeyNotFound: If the key was not found in local database.
        """
        leap_assert(
            ktype is OpenPGPKey,
            'For now we only know how to send OpenPGP public keys.')
        # prepare the public key bound to address
        pubkey = self.get_key(
            self._address, ktype, private=False, fetch_remote=False)
        data = {
            self.PUBKEY_KEY: pubkey.key_data
        }
        uri = "%s/%s/users/%s.json" % (
            self._api_uri,
            self._api_version,
            self._uid)
        self._put(uri, data)
        signal(proto.KEYMANAGER_DONE_UPLOADING_KEYS, self._address)
예제 #6
0
 def connectionLost(self):
     """
     Log an error when the connection is lost.
     """
     log.msg("Connection lost unexpectedly!")
     log.err()
     signal(proto.SMTP_CONNECTION_LOST, self._user.dest.addrstr)
     # unexpected loss of connection; don't save
     self.lines = []
예제 #7
0
    def sendSuccess(self, r):
        """
        Callback for a successful send.

        :param r: The result from the last previous callback in the chain.
        :type r: anything
        """
        log.msg(r)
        signal(proto.SMTP_SEND_MESSAGE_SUCCESS, self._user.dest.addrstr)
예제 #8
0
    def sendSuccess(self, r):
        """
        Callback for a successful send.

        :param r: The result from the last previous callback in the chain.
        :type r: anything
        """
        log.msg(r)
        signal(proto.SMTP_SEND_MESSAGE_SUCCESS, self._user.dest.addrstr)
예제 #9
0
 def connectionLost(self):
     """
     Log an error when the connection is lost.
     """
     log.msg("Connection lost unexpectedly!")
     log.err()
     signal(proto.SMTP_CONNECTION_LOST, self._user.dest.addrstr)
     # unexpected loss of connection; don't save
     self.lines = []
예제 #10
0
    def sendError(self, failure):
        """
        Callback for an unsuccessfull send.

        :param e: The result from the last errback.
        :type e: anything
        """
        signal(proto.SMTP_SEND_MESSAGE_ERROR, self._user.dest.addrstr)
        err = failure.value
        log.err(err)
        raise err
예제 #11
0
    def sendError(self, failure):
        """
        Callback for an unsuccessfull send.

        :param e: The result from the last errback.
        :type e: anything
        """
        signal(proto.SMTP_SEND_MESSAGE_ERROR, self._user.dest.addrstr)
        err = failure.value
        log.err(err)
        raise err
예제 #12
0
def setup_smtp_relay(port, keymanager, smtp_host, smtp_port,
                     smtp_cert, smtp_key, encrypted_only):
    """
    Setup SMTP relay to run with Twisted.

    This function sets up the SMTP relay configuration and the Twisted
    reactor.

    @param port: The port in which to run the server.
    @type port: int
    @param keymanager: A Key Manager from where to get recipients' public
        keys.
    @type keymanager: leap.common.keymanager.KeyManager
    @param smtp_host: The hostname of the remote SMTP server.
    @type smtp_host: str
    @param smtp_port:  The port of the remote SMTP server.
    @type smtp_port: int
    @param smtp_cert: The client certificate for authentication.
    @type smtp_cert: str
    @param smtp_key: The client key for authentication.
    @type smtp_key: str
    @param encrypted_only: Whether the SMTP relay should send unencrypted mail
        or not.
    @type encrypted_only: bool
    """
    # The configuration for the SMTP relay is a dict with the following
    # format:
    #
    # {
    #     'host': '<host>',
    #     'port': <int>,
    #     'cert': '<cert path>',
    #     'key': '<key path>',
    #     'encrypted_only': <True/False>
    # }
    config = {
        'host': smtp_host,
        'port': smtp_port,
        'cert': smtp_cert,
        'key': smtp_key,
        'encrypted_only': encrypted_only
    }

    # configure the use of this service with twistd
    factory = SMTPFactory(keymanager, config)
    try:
        reactor.listenTCP(port, factory)
        signal(proto.SMTP_SERVICE_STARTED, str(smtp_port))
    except CannotListenError:
        logger.error("STMP Service failed to start: "
                     "cannot listen in port %s" % (
                         smtp_port,))
        signal(proto.SMTP_SERVICE_FAILED_TO_START, str(smtp_port))
예제 #13
0
 def _decrypt_msg(self, doc, encdata):
     log.msg('decrypting msg')
     key = self._pkey
     try:
         decrdata = (self._keymanager.decrypt(
             encdata, key,
             passphrase=self._soledad.passphrase))
         ok = True
     except Exception as exc:
         # XXX move this to errback !!!
         logger.warning("Error while decrypting msg: %r" % (exc,))
         decrdata = ""
         ok = False
     leap_events.signal(IMAP_MSG_DECRYPTED, "1" if ok else "0")
     return doc, decrdata
예제 #14
0
    def gen_key(self, ktype):
        """
        Generate a key of type C{ktype} bound to the user's address.

        :param ktype: The type of the key.
        :type ktype: KeyType

        :return: The generated key.
        :rtype: EncryptionKey
        """
        signal(proto.KEYMANAGER_STARTED_KEY_GENERATION, self._address)
        key = self._wrapper_map[ktype].gen_key(self._address)
        signal(proto.KEYMANAGER_FINISHED_KEY_GENERATION, self._address)

        return key
예제 #15
0
파일: fetch.py 프로젝트: micah/leap_mail
    def _signal_fetch_to_ui(self, doclist):
        """
        Sends leap events to ui.

        :param doclist: iterable with msg documents.
        :type doclist: iterable.
        :returns: doclist
        :rtype: iterable
        """
        fetched_ts = time.mktime(time.gmtime())
        num_mails = len(doclist)
        log.msg("there are %s mails" % (num_mails,))
        leap_events.signal(
            IMAP_FETCHED_INCOMING, str(num_mails), str(fetched_ts))
        self._signal_unread_to_ui()
        return doclist
예제 #16
0
파일: fetch.py 프로젝트: pmaia/leap_mail
    def _sync_soledad(self):
        """
        Synchronize with remote soledad.

        :returns: a list of LeapDocuments, or None.
        :rtype: iterable or None
        """
        with self.fetching_lock:
            try:
                log.msg('FETCH: syncing soledad...')
                self._soledad.sync()
                log.msg('FETCH soledad SYNCED.')
            except InvalidAuthTokenError:
                # if the token is invalid, send an event so the GUI can
                # disable mail and show an error message.
                leap_events.signal(SOLEDAD_INVALID_AUTH_TOKEN)
예제 #17
0
 def test_component_send_signal(self):
     """
     Ensure components can send signals.
     """
     sig = 8
     response = events.signal(sig)
     self.assertTrue(response.status == response.OK,
                     'Received wrong response status when signaling.')
예제 #18
0
 def test_client_send_signal(self):
     """
     Ensure clients can send signals.
     """
     sig = 8
     response = events.signal(sig)
     self.assertTrue(response.status == response.OK,
                     'Received wrong response status when signaling.')
예제 #19
0
    def _add_message_locally(self, msgtuple):
        """
        Adds a message to local inbox and delete it from the incoming db
        in soledad.

        :param msgtuple: a tuple consisting of a SoledadDocument
                         instance containing the incoming message
                         and data, the json-encoded, decrypted content of the
                         incoming message
        :type msgtuple: (SoledadDocument, str)
        """
        doc, data = msgtuple
        self._inbox.addMessage(data, (self.RECENT_FLAG,))
        leap_events.signal(IMAP_MSG_SAVED_LOCALLY)
        doc_id = doc.doc_id
        self._soledad.delete_doc(doc)
        log.msg("deleted doc %s from incoming" % doc_id)
        leap_events.signal(IMAP_MSG_DELETED_INCOMING)
예제 #20
0
파일: server.py 프로젝트: pmaia/leap_mail
    def authenticateLogin(self, username, password):
        """
        Lookup the account with the given parameters, and deny
        the improper combinations.

        :param username: the username that is attempting authentication.
        :type username: str
        :param password: the password to authenticate with.
        :type password: str
        """
        # XXX this should use portal:
        # return portal.login(cred.credentials.UsernamePassword(user, pass)
        if username != self._userid:
            # bad username, reject.
            raise cred.error.UnauthorizedLogin()
        # any dummy password is allowed so far. use realm instead!
        leap_events.signal(IMAP_CLIENT_LOGIN, "1")
        return imap4.IAccount, self.theAccount, lambda: None
예제 #21
0
파일: fetch.py 프로젝트: pmaia/leap_mail
    def _signal_fetch_to_ui(self, doclist):
        """
        Send leap events to ui.

        :param doclist: iterable with msg documents.
        :type doclist: iterable.
        :returns: doclist
        :rtype: iterable
        """
        doclist = first(doclist)  # gatherResults pass us a list
        if doclist:
            fetched_ts = time.mktime(time.gmtime())
            num_mails = len(doclist) if doclist is not None else 0
            if num_mails != 0:
                log.msg("there are %s mails" % (num_mails,))
            leap_events.signal(
                IMAP_FETCHED_INCOMING, str(num_mails), str(fetched_ts))
            return doclist
예제 #22
0
    def authenticateLogin(self, username, password):
        """
        Lookup the account with the given parameters, and deny
        the improper combinations.

        :param username: the username that is attempting authentication.
        :type username: str
        :param password: the password to authenticate with.
        :type password: str
        """
        # XXX this should use portal:
        # return portal.login(cred.credentials.UsernamePassword(user, pass)
        if username != self._userid:
            # bad username, reject.
            raise cred.error.UnauthorizedLogin()
        # any dummy password is allowed so far. use realm instead!
        leap_events.signal(IMAP_CLIENT_LOGIN, "1")
        return imap4.IAccount, self.theAccount, lambda: None
예제 #23
0
파일: fetch.py 프로젝트: pmaia/leap_mail
    def _process_doclist(self, doclist):
        """
        Iterates through the doclist, checks if each doc
        looks like a message, and yields a deferred that will decrypt and
        process the message.

        :param doclist: iterable with msg documents.
        :type doclist: iterable.
        :returns: a list of deferreds for individual messages.
        """
        log.msg('processing doclist')
        if not doclist:
            logger.debug("no docs found")
            return
        num_mails = len(doclist)

        for index, doc in enumerate(doclist):
            logger.debug("processing doc %d of %d" % (index + 1, num_mails))
            leap_events.signal(
                IMAP_MSG_PROCESSING, str(index), str(num_mails))

            keys = doc.content.keys()

            # TODO Compatibility check with the index in pre-0.6 mx
            # that does not write the ERROR_DECRYPTING_KEY
            # This should be removed in 0.7

            has_errors = doc.content.get(fields.ERROR_DECRYPTING_KEY, None)
            if has_errors is None:
                warnings.warn("JUST_MAIL_COMPAT_IDX will be deprecated!",
                              DeprecationWarning)
            if has_errors:
                logger.debug("skipping msg with decrypting errors...")

            if self._is_msg(keys) and not has_errors:
                # Evaluating to bool of has_errors is intentional here.
                # We don't mind at this point if it's None or False.

                # Ok, this looks like a legit msg, and with no errors.
                # Let's process it!

                d1 = self._decrypt_doc(doc)
                d = defer.gatherResults([d1], consumeErrors=True)
                d.addCallbacks(self._add_message_locally, self._errback)
예제 #24
0
    def get_key(self, address, ktype, private=False, fetch_remote=True):
        """
        Return a key of type C{ktype} bound to C{address}.

        First, search for the key in local storage. If it is not available,
        then try to fetch from nickserver.

        :param address: The address bound to the key.
        :type address: str
        :param ktype: The type of the key.
        :type ktype: KeyType
        :param private: Look for a private key instead of a public one?
        :type private: bool

        :return: A key of type C{ktype} bound to C{address}.
        :rtype: EncryptionKey
        @raise KeyNotFound: If the key was not found both locally and in
            keyserver.
        """
        leap_assert(
            ktype in self._wrapper_map,
            'Unkown key type: %s.' % str(ktype))
        try:
            signal(proto.KEYMANAGER_LOOKING_FOR_KEY, address)
            # return key if it exists in local database
            key = self._wrapper_map[ktype].get_key(address, private=private)
            signal(proto.KEYMANAGER_KEY_FOUND, address)

            return key
        except KeyNotFound:
            signal(proto.KEYMANAGER_KEY_NOT_FOUND, address)

            # we will only try to fetch a key from nickserver if fetch_remote
            # is True and the key is not private.
            if fetch_remote is False or private is True:
                raise

            signal(proto.KEYMANAGER_LOOKING_FOR_KEY, address)
            self._fetch_keys_from_server(address)
            key = self._wrapper_map[ktype].get_key(address, private=False)
            signal(proto.KEYMANAGER_KEY_FOUND, address)

            return key
예제 #25
0
def setup_smtp_gateway(port, userid, keymanager, smtp_host, smtp_port,
                       smtp_cert, smtp_key, encrypted_only):
    """
    Setup SMTP gateway to run with Twisted.

    This function sets up the SMTP gateway configuration and the Twisted
    reactor.

    :param port: The port in which to run the server.
    :type port: int
    :param userid: The user currently logged in
    :type userid: unicode
    :param keymanager: A Key Manager from where to get recipients' public
                       keys.
    :type keymanager: leap.common.keymanager.KeyManager
    :param smtp_host: The hostname of the remote SMTP server.
    :type smtp_host: str
    :param smtp_port: The port of the remote SMTP server.
    :type smtp_port: int
    :param smtp_cert: The client certificate for authentication.
    :type smtp_cert: str
    :param smtp_key: The client key for authentication.
    :type smtp_key: str
    :param encrypted_only: Whether the SMTP gateway should send unencrypted
                           mail or not.
    :type encrypted_only: bool

    :returns: tuple of SMTPFactory, twisted.internet.tcp.Port
    """
    # configure the use of this service with twistd
    factory = SMTPFactory(userid, keymanager, smtp_host, smtp_port, smtp_cert,
                          smtp_key, encrypted_only)
    try:
        tport = reactor.listenTCP(port, factory, interface="localhost")
        signal(proto.SMTP_SERVICE_STARTED, str(port))
        return factory, tport
    except CannotListenError:
        logger.error("STMP Service failed to start: "
                     "cannot listen in port %s" % port)
        signal(proto.SMTP_SERVICE_FAILED_TO_START, str(port))
    except Exception as exc:
        logger.error("Unhandled error while launching smtp gateway service")
        logger.exception(exc)
예제 #26
0
파일: imap.py 프로젝트: micah/leap_mail
def run_service(*args, **kwargs):
    """
    Main entry point to run the service from the client.

    :returns: the LoopingCall instance that will have to be stoppped
              before shutting down the client.
    """
    leap_assert(len(args) == 2)
    soledad, keymanager = args
    leap_assert_type(soledad, Soledad)
    leap_assert_type(keymanager, KeyManager)

    port = kwargs.get('port', IMAP_PORT)
    check_period = kwargs.get('check_period', INCOMING_CHECK_PERIOD)

    uuid = soledad._get_uuid()
    factory = LeapIMAPFactory(uuid, soledad)

    from twisted.internet import reactor

    try:
        reactor.listenTCP(port, factory,
                          interface="localhost")
        fetcher = LeapIncomingMail(
            keymanager,
            soledad,
            factory.theAccount,
            check_period)
    except CannotListenError:
        logger.error("IMAP Service failed to start: "
                     "cannot listen in port %s" % (port,))
    except Exception as exc:
        logger.error("Error launching IMAP service: %r" % (exc,))
    else:
        # all good.
        fetcher.start_loop()
        logger.debug("IMAP4 Server is RUNNING in port  %s" % (port,))
        leap_events.signal(IMAP_SERVICE_STARTED, str(port))
        return fetcher

    # not ok, signal error.
    leap_events.signal(IMAP_SERVICE_FAILED_TO_START, str(port))
예제 #27
0
    def test_async_register(self):
        """
        Test asynchronous registering of callbacks.
        """
        flag = Mock()

        # executed after async register, when response is received from server
        def reqcbk(request, response):
            flag(request.event, response.status)

        # callback registered by application
        def callback(request):
            pass

        # passing a callback as reqcbk param makes the call asynchronous
        result = events.register(CLIENT_UID, callback, reqcbk=reqcbk)
        self.assertIsNone(result)
        events.signal(CLIENT_UID)
        time.sleep(1)  # wait for signal to arrive from server
        flag.assert_called_once_with(CLIENT_UID, EventResponse.OK)
예제 #28
0
    def run(self):
        """
        Check for updates periodically
        """
        if not self.mirrors:
            return

        while True:
            try:
                tuf.conf.repository_directory = os.path.join(
                    self.bundle_path, 'repo')

                updater = tuf.client.updater.Updater('leap-updater',
                                                     self.mirrors)
                updater.refresh()

                targets = updater.all_targets()
                updated_targets = updater.updated_targets(
                    targets, self.source_path)
                if updated_targets:
                    print "There is updates needed. Start downloading updates."
                for target in updated_targets:
                    updater.download_target(target, self.dest_path)
                    self._set_permissions(target)
                if os.path.isdir(self.dest_path):
                    if os.path.isdir(self.update_path):
                        shutil.rmtree(self.update_path)
                    shutil.move(self.dest_path, self.update_path)
                    filepath = sorted([f['filepath'] for f in updated_targets])
                    signal(proto.UPDATER_NEW_UPDATES,
                           content=", ".join(filepath))
                    print "Updates ready: ", filepath
                    return
            except NotImplemented as e:
                print "NotImplemented: ", e
                return
            except Exception as e:
                print "ERROR:", e
            finally:
                time.sleep(self.delay)
예제 #29
0
    def _process_doclist(self, doclist):
        """
        Iterates through the doclist, checks if each doc
        looks like a message, and yields a deferred that will decrypt and
        process the message.

        :param doclist: iterable with msg documents.
        :type doclist: iterable.
        :returns: a list of deferreds for individual messages.
        """
        log.msg('processing doclist')
        if not doclist:
            logger.debug("no docs found")
            return
        num_mails = len(doclist)

        docs_cb = []
        for index, doc in enumerate(doclist):
            logger.debug("processing doc %d of %d: %s" % (
                index, num_mails, doc))
            leap_events.signal(
                IMAP_MSG_PROCESSING, str(index), str(num_mails))
            keys = doc.content.keys()
            if self._is_msg(keys):
                # Ok, this looks like a legit msg.
                # Let's process it!
                encdata = doc.content[ENC_JSON_KEY]

                # Deferred chain for individual messages
                d = deferToThread(self._decrypt_msg, doc, encdata)
                d.addCallback(self._process_decrypted)
                d.addCallback(self._add_message_locally)
                docs_cb.append(d)
            else:
                # Ooops, this does not.
                logger.debug('This does not look like a proper msg.')
        return docs_cb
예제 #30
0
    def validateTo(self, user):
        """
        Validate the address of C{user}, a recipient of the message.

        This method is called once for each recipient and validates the
        C{user}'s address against the RFC 2822 definition. If the
        configuration option ENCRYPTED_ONLY_KEY is True, it also asserts the
        existence of the user's key.

        In the end, it returns an encrypted message object that is able to
        send itself to the C{user}'s address.

        :param user: The user whose address we wish to validate.
        :type: twisted.mail.smtp.User

        @return: A Deferred which becomes, or a callable which takes no
            arguments and returns an object implementing IMessage. This will
            be called and the returned object used to deliver the message when
            it arrives.
        @rtype: no-argument callable

        @raise SMTPBadRcpt: Raised if messages to the address are not to be
            accepted.
        """
        # try to find recipient's public key
        try:
            address = validate_address(user.dest.addrstr)
            # verify if recipient key is available in keyring
            self._km.get_key(address, OpenPGPKey)  # might raise KeyNotFound
            log.msg("Accepting mail for %s..." % user.dest.addrstr)
            signal(proto.SMTP_RECIPIENT_ACCEPTED_ENCRYPTED, user.dest.addrstr)
        except KeyNotFound:
            # if key was not found, check config to see if will send anyway.
            if self._encrypted_only:
                signal(proto.SMTP_RECIPIENT_REJECTED, user.dest.addrstr)
                raise smtp.SMTPBadRcpt(user.dest.addrstr)
            log.msg("Warning: will send an unencrypted message (because "
                    "encrypted_only' is set to False).")
            signal(
                proto.SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED, user.dest.addrstr)
        return lambda: EncryptedMessage(
            self._origin, user, self._km, self._host, self._port, self._cert,
            self._key)
예제 #31
0
    def validateTo(self, user):
        """
        Validate the address of C{user}, a recipient of the message.

        This method is called once for each recipient and validates the
        C{user}'s address against the RFC 2822 definition. If the
        configuration option ENCRYPTED_ONLY_KEY is True, it also asserts the
        existence of the user's key.

        In the end, it returns an encrypted message object that is able to
        send itself to the C{user}'s address.

        :param user: The user whose address we wish to validate.
        :type: twisted.mail.smtp.User

        @return: A Deferred which becomes, or a callable which takes no
            arguments and returns an object implementing IMessage. This will
            be called and the returned object used to deliver the message when
            it arrives.
        @rtype: no-argument callable

        @raise SMTPBadRcpt: Raised if messages to the address are not to be
            accepted.
        """
        # try to find recipient's public key
        try:
            address = validate_address(user.dest.addrstr)
            # verify if recipient key is available in keyring
            self._km.get_key(address, OpenPGPKey)  # might raise KeyNotFound
            log.msg("Accepting mail for %s..." % user.dest.addrstr)
            signal(proto.SMTP_RECIPIENT_ACCEPTED_ENCRYPTED, user.dest.addrstr)
        except KeyNotFound:
            # if key was not found, check config to see if will send anyway.
            if self._encrypted_only:
                signal(proto.SMTP_RECIPIENT_REJECTED, user.dest.addrstr)
                raise smtp.SMTPBadRcpt(user.dest.addrstr)
            log.msg("Warning: will send an unencrypted message (because "
                    "encrypted_only' is set to False).")
            signal(proto.SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED,
                   user.dest.addrstr)
        return lambda: EncryptedMessage(self._origin, user, self._km, self.
                                        _host, self._port, self._cert, self.
                                        _key)
예제 #32
0
파일: server.py 프로젝트: micah/leap_mail
 def _signal_unread_to_ui(self):
     """
     Sends unread event to ui.
     """
     leap_events.signal(
         IMAP_UNREAD_MAIL, str(self.getUnseenCount()))
예제 #33
0
    def _maybe_encrypt_and_sign(self):
        """
        Attempt to encrypt and sign the outgoing message.

        The behaviour of this method depends on:

            1. the original message's content-type, and
            2. the availability of the recipient's public key.

        If the original message's content-type is "multipart/encrypted", then
        the original message is not altered. For any other content-type, the
        method attempts to fetch the recipient's public key. If the
        recipient's public key is available, the message is encrypted and
        signed; otherwise it is only signed.

        Note that, if the C{encrypted_only} configuration is set to True and
        the recipient's public key is not available, then the recipient
        address would have been rejected in SMTPDelivery.validateTo().

        The following table summarizes the overall behaviour of the gateway:

        +---------------------------------------------------+----------------+
        | content-type        | rcpt pubkey | enforce encr. | action         |
        +---------------------+-------------+---------------+----------------+
        | multipart/encrypted | any         | any           | pass           |
        | other               | available   | any           | encrypt + sign |
        | other               | unavailable | yes           | reject         |
        | other               | unavailable | no            | sign           |
        +---------------------+-------------+---------------+----------------+
        """
        # pass if the original message's content-type is "multipart/encrypted"
        self._origmsg = self.parseMessage()
        if self._origmsg.get_content_type() == 'multipart/encrypted':
            self._msg = self._origmsg
            return

        from_address = validate_address(self._fromAddress.addrstr)
        username, domain = from_address.split('@')

        # add a nice footer to the outgoing message
        if self._origmsg.get_content_type() == 'text/plain':
            self.lines.append('--')
            self.lines.append('%s - https://%s/key/%s' %
                              (self.FOOTER_STRING, domain, username))
            self.lines.append('')

        self._origmsg = self.parseMessage()

        # get sender and recipient data
        signkey = self._km.get_key(from_address, OpenPGPKey, private=True)
        log.msg("Will sign the message with %s." % signkey.fingerprint)
        to_address = validate_address(self._user.dest.addrstr)
        try:
            # try to get the recipient pubkey
            pubkey = self._km.get_key(to_address, OpenPGPKey)
            log.msg("Will encrypt the message to %s." % pubkey.fingerprint)
            signal(proto.SMTP_START_ENCRYPT_AND_SIGN,
                   "%s,%s" % (self._fromAddress.addrstr, to_address))
            self._encrypt_and_sign(pubkey, signkey)
            signal(proto.SMTP_END_ENCRYPT_AND_SIGN,
                   "%s,%s" % (self._fromAddress.addrstr, to_address))
        except KeyNotFound:
            # at this point we _can_ send unencrypted mail, because if the
            # configuration said the opposite the address would have been
            # rejected in SMTPDelivery.validateTo().
            log.msg('Will send unencrypted message to %s.' % to_address)
            signal(proto.SMTP_START_SIGN, self._fromAddress.addrstr)
            self._sign(signkey)
            signal(proto.SMTP_END_SIGN, self._fromAddress.addrstr)
예제 #34
0
파일: fetch.py 프로젝트: pmaia/leap_mail
 def msgSavedCallback(result):
     if not empty(result):
         leap_events.signal(IMAP_MSG_SAVED_LOCALLY)
         deferLater(reactor, 0, self._delete_incoming_message, doc)
         leap_events.signal(IMAP_MSG_DELETED_INCOMING)
예제 #35
0
파일: fetch.py 프로젝트: pmaia/leap_mail
 def _signal_unread_to_ui(self, *args):
     """
     Sends unread event to ui.
     """
     leap_events.signal(
         IMAP_UNREAD_MAIL, str(self._inbox.getUnseenCount()))
예제 #36
0
def run_service(*args, **kwargs):
    """
    Main entry point to run the service from the client.

    :returns: the LoopingCall instance that will have to be stoppped
              before shutting down the client, the port as returned by
              the reactor when starts listening, and the factory for
              the protocol.
    """
    from twisted.internet import reactor
    # it looks like qtreactor does not honor this,
    # but other reactors should.
    reactor.suggestThreadPoolSize(20)

    leap_assert(len(args) == 2)
    soledad, keymanager = args
    leap_assert_type(soledad, Soledad)
    leap_assert_type(keymanager, KeyManager)

    port = kwargs.get('port', IMAP_PORT)
    check_period = kwargs.get('check_period', INCOMING_CHECK_PERIOD)
    userid = kwargs.get('userid', None)
    leap_check(userid is not None, "need an user id")
    offline = kwargs.get('offline', False)

    uuid = soledad._get_uuid()
    factory = LeapIMAPFactory(uuid, userid, soledad)

    try:
        tport = reactor.listenTCP(port, factory,
                                  interface="localhost")
        if not offline:
            fetcher = LeapIncomingMail(
                keymanager,
                soledad,
                factory.theAccount,
                check_period,
                userid)
        else:
            fetcher = None
    except CannotListenError:
        logger.error("IMAP Service failed to start: "
                     "cannot listen in port %s" % (port,))
    except Exception as exc:
        logger.error("Error launching IMAP service: %r" % (exc,))
    else:
        # all good.
        # (the caller has still to call fetcher.start_loop)

        if DO_MANHOLE:
            # TODO get pass from env var.too.
            manhole_factory = manhole.getManholeFactory(
                {'f': factory,
                 'a': factory.theAccount,
                 'gm': factory.theAccount.getMailbox},
                "boss", "leap")
            reactor.listenTCP(manhole.MANHOLE_PORT, manhole_factory,
                              interface="127.0.0.1")
        logger.debug("IMAP4 Server is RUNNING in port  %s" % (port,))
        leap_events.signal(IMAP_SERVICE_STARTED, str(port))
        return fetcher, tport, factory

    # not ok, signal error.
    leap_events.signal(IMAP_SERVICE_FAILED_TO_START, str(port))
예제 #37
0
    def _maybe_encrypt_and_sign(self):
        """
        Attempt to encrypt and sign the outgoing message.

        The behaviour of this method depends on:

            1. the original message's content-type, and
            2. the availability of the recipient's public key.

        If the original message's content-type is "multipart/encrypted", then
        the original message is not altered. For any other content-type, the
        method attempts to fetch the recipient's public key. If the
        recipient's public key is available, the message is encrypted and
        signed; otherwise it is only signed.

        Note that, if the C{encrypted_only} configuration is set to True and
        the recipient's public key is not available, then the recipient
        address would have been rejected in SMTPDelivery.validateTo().

        The following table summarizes the overall behaviour of the gateway:

        +---------------------------------------------------+----------------+
        | content-type        | rcpt pubkey | enforce encr. | action         |
        +---------------------+-------------+---------------+----------------+
        | multipart/encrypted | any         | any           | pass           |
        | other               | available   | any           | encrypt + sign |
        | other               | unavailable | yes           | reject         |
        | other               | unavailable | no            | sign           |
        +---------------------+-------------+---------------+----------------+
        """
        # pass if the original message's content-type is "multipart/encrypted"
        self._origmsg = self.parseMessage()
        if self._origmsg.get_content_type() == 'multipart/encrypted':
            self._msg = self._origmsg
            return

        from_address = validate_address(self._fromAddress.addrstr)
        username, domain = from_address.split('@')

        # add a nice footer to the outgoing message
        if self._origmsg.get_content_type() == 'text/plain':
            self.lines.append('--')
            self.lines.append('%s - https://%s/key/%s' %
                              (self.FOOTER_STRING, domain, username))
            self.lines.append('')

        self._origmsg = self.parseMessage()

        # get sender and recipient data
        signkey = self._km.get_key(from_address, OpenPGPKey, private=True)
        log.msg("Will sign the message with %s." % signkey.fingerprint)
        to_address = validate_address(self._user.dest.addrstr)
        try:
            # try to get the recipient pubkey
            pubkey = self._km.get_key(to_address, OpenPGPKey)
            log.msg("Will encrypt the message to %s." % pubkey.fingerprint)
            signal(proto.SMTP_START_ENCRYPT_AND_SIGN,
                   "%s,%s" % (self._fromAddress.addrstr, to_address))
            self._encrypt_and_sign(pubkey, signkey)
            signal(proto.SMTP_END_ENCRYPT_AND_SIGN,
                   "%s,%s" % (self._fromAddress.addrstr, to_address))
        except KeyNotFound:
            # at this point we _can_ send unencrypted mail, because if the
            # configuration said the opposite the address would have been
            # rejected in SMTPDelivery.validateTo().
            log.msg('Will send unencrypted message to %s.' % to_address)
            signal(proto.SMTP_START_SIGN, self._fromAddress.addrstr)
            self._sign(signkey)
            signal(proto.SMTP_END_SIGN, self._fromAddress.addrstr)
예제 #38
0
파일: imap.py 프로젝트: micah/leap_mail
 def authenticateLogin(self, username, password):
     # all is allowed so far. use realm instead
     leap_events.signal(IMAP_CLIENT_LOGIN, "1")
     return imap4.IAccount, self.theAccount, lambda: None