def _route_msg(self, encrypt_and_sign_result): """ Sends the msg using the ESMTPSenderFactory. :param encrypt_and_sign_result: A tuple containing the 'maybe' encrypted message and the recipient :type encrypt_and_sign_result: tuple """ message, recipient = encrypt_and_sign_result log.msg("Connecting to SMTP server %s:%s" % (self._host, self._port)) msg = message.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, no client auth here "", # password is blank, no client auth here self._from_address, recipient.dest.addrstr, StringIO(msg), d, heloFallback=True, requireAuthentication=False, requireTransportSecurity=True) factory.domain = __version__ emit(catalog.SMTP_SEND_MESSAGE_START, recipient.dest.addrstr) reactor.connectSSL(self._host, self._port, factory, contextFactory=SSLContextFactory( self._cert, self._key))
def _extract_data(self, json_content): """ Extracts the necessary parameters from json_content (M2, id, token) Might raise SRPAuthenticationError based: SRPBadDataFromServer :param json_content: Data received from the server :type json_content: dict """ try: M2 = json_content.get("M2", None) uuid = json_content.get("id", None) token = json_content.get("token", None) except Exception as e: logger.error(e) raise SRPAuthBadDataFromServer() self.set_uuid(uuid) self.set_token(token) if M2 is None or self.get_uuid() is None: logger.error("Something went wrong. Content = %r" % (json_content, )) raise SRPAuthBadDataFromServer() emit(catalog.CLIENT_UID, uuid) # make the rpc call async return M2
def _route_msg(self, encrypt_and_sign_result): """ Sends the msg using the ESMTPSenderFactory. :param encrypt_and_sign_result: A tuple containing the 'maybe' encrypted message and the recipient :type encrypt_and_sign_result: tuple """ message, recipient = encrypt_and_sign_result log.msg("Connecting to SMTP server %s:%s" % (self._host, self._port)) msg = message.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, no client auth here "", # password is blank, no client auth here self._from_address, recipient.dest.addrstr, StringIO(msg), d, heloFallback=True, requireAuthentication=False, requireTransportSecurity=True) factory.domain = __version__ emit(catalog.SMTP_SEND_MESSAGE_START, recipient.dest.addrstr) reactor.connectSSL( self._host, self._port, factory, contextFactory=SSLContextFactory(self._cert, self._key))
def _extract_data(self, json_content): """ Extracts the necessary parameters from json_content (M2, id, token) Might raise SRPAuthenticationError based: SRPBadDataFromServer :param json_content: Data received from the server :type json_content: dict """ try: M2 = json_content.get("M2", None) uuid = json_content.get("id", None) token = json_content.get("token", None) except Exception as e: logger.error(e) raise SRPAuthBadDataFromServer() self.set_uuid(uuid) self.set_token(token) if M2 is None or self.get_uuid() is None: logger.error("Something went wrong. Content = %r" % (json_content,)) raise SRPAuthBadDataFromServer() emit(catalog.CLIENT_UID, uuid) # make the rpc call async return M2
def __cb_signal_unread_to_ui(self, unseen): """ Send the unread signal to UI. :param unseen: number of unseen messages. :type unseen: int """ # TODO change name of the signal, independent from imap now. emit(catalog.MAIL_UNREAD_MESSAGES, str(unseen))
def if_key_not_found_send_unencrypted(failure, message): failure.trap(KeyNotFound, KeyAddressMismatch) log.msg('Will send unencrypted message to %s.' % to_address) emit(catalog.SMTP_START_SIGN, self._from_address) d = self._sign(message, from_address) d.addCallback(signal_sign) return d
def get_key(self, address, ktype, private=False, fetch_remote=True): """ Return a key of type ktype bound to 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: subclass of EncryptionKey :param private: Look for a private key instead of a public one? :type private: bool :param fetch_remote: If key not found in local storage try to fetch from nickserver :type fetch_remote: bool :return: A Deferred which fires with an EncryptionKey of type ktype bound to address, or which fails with KeyNotFound if no key was found neither locally or in keyserver. :rtype: Deferred :raise UnsupportedKeyTypeError: if invalid key type """ self._assert_supported_key_type(ktype) logger.debug("getting key for %s" % (address,)) leap_assert( ktype in self._wrapper_map, 'Unkown key type: %s.' % str(ktype)) emit(catalog.KEYMANAGER_LOOKING_FOR_KEY, address) def key_found(key): emit(catalog.KEYMANAGER_KEY_FOUND, address) return key def key_not_found(failure): if not failure.check(KeyNotFound): return failure emit(catalog.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: return failure emit(catalog.KEYMANAGER_LOOKING_FOR_KEY, address) d = self._fetch_keys_from_server(address) d.addCallback( lambda _: self._wrapper_map[ktype].get_key(address, private=False)) d.addCallback(key_found) return d # return key if it exists in local database d = self._wrapper_map[ktype].get_key(address, private=private) d.addCallbacks(key_found, key_not_found) return d
def get_key(self, address, ktype, private=False, fetch_remote=True): """ Return a key of type ktype bound to 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: subclass of EncryptionKey :param private: Look for a private key instead of a public one? :type private: bool :param fetch_remote: If key not found in local storage try to fetch from nickserver :type fetch_remote: bool :return: A Deferred which fires with an EncryptionKey of type ktype bound to address, or which fails with KeyNotFound if no key was found neither locally or in keyserver. :rtype: Deferred :raise UnsupportedKeyTypeError: if invalid key type """ self._assert_supported_key_type(ktype) logger.debug("getting key for %s" % (address, )) leap_assert(ktype in self._wrapper_map, 'Unkown key type: %s.' % str(ktype)) emit(catalog.KEYMANAGER_LOOKING_FOR_KEY, address) def key_found(key): emit(catalog.KEYMANAGER_KEY_FOUND, address) return key def key_not_found(failure): if not failure.check(KeyNotFound): return failure emit(catalog.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: return failure emit(catalog.KEYMANAGER_LOOKING_FOR_KEY, address) d = self._fetch_keys_from_server(address) d.addCallback(lambda _: self._wrapper_map[ktype].get_key( address, private=False)) d.addCallback(key_found) return d # return key if it exists in local database d = self._wrapper_map[ktype].get_key(address, private=private) d.addCallbacks(key_found, key_not_found) return d
def connectionLost(self): """ Log an error when the connection is lost. """ log.msg("Connection lost unexpectedly!") log.err() emit(catalog.SMTP_CONNECTION_LOST, self._user.dest.addrstr) # unexpected loss of connection; don't save self._lines = []
def process_decrypted(res): if isinstance(res, tuple): decrdata, _ = res success = True else: decrdata = "" success = False emit(catalog.MAIL_MSG_DECRYPTED, "1" if success else "0") return self._process_decrypted_doc(doc, decrdata)
def send(pubkey): 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) emit(catalog.KEYMANAGER_DONE_UPLOADING_KEYS, self._address)
def sendSuccess(self, smtp_sender_result): """ Callback for a successful send. :param smtp_sender_result: The result from the ESMTPSender from _route_msg :type smtp_sender_result: tuple(int, list(tuple)) """ dest_addrstr = smtp_sender_result[1][0][0] log.msg('Message sent to %s' % dest_addrstr) emit(catalog.SMTP_SEND_MESSAGE_SUCCESS, dest_addrstr)
def not_found(failure): failure.trap(KeyNotFound) # if key was not found, check config to see if will send anyway if self._encrypted_only: emit(catalog.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).") emit(catalog.SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED, user.dest.addrstr)
def not_found(failure): failure.trap(KeyNotFound) # if key was not found, check config to see if will send anyway if self._encrypted_only: emit(catalog.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).") emit( catalog.SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED, user.dest.addrstr)
def run_service(store, **kwargs): """ Main entry point to run the service from the client. :param store: a soledad instance :returns: the port as returned by the reactor when starts listening, and the factory for the protocol. """ leap_check(store, "store cannot be None") # XXX this can also be a ProxiedObject, FIXME # leap_assert_type(store, Soledad) port = kwargs.get('port', IMAP_PORT) userid = kwargs.get('userid', None) leap_check(userid is not None, "need an user id") uuid = store.uuid factory = LeapIMAPFactory(uuid, userid, store) try: tport = reactor.listenTCP(port, factory, interface="localhost") 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. 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, )) emit(catalog.IMAP_SERVICE_STARTED, str(port)) # FIXME -- change service signature return tport, factory # not ok, signal error. emit(catalog.IMAP_SERVICE_FAILED_TO_START, str(port))
def msgSavedCallback(result): if empty(result): return for listener in self._listeners: listener(result) def signal_deleted(doc_id): emit(catalog.MAIL_MSG_DELETED_INCOMING) return doc_id emit(catalog.MAIL_MSG_SAVED_LOCALLY) d = self._delete_incoming_message(doc) d.addCallback(signal_deleted) return d
def run_service(store, **kwargs): """ Main entry point to run the service from the client. :param store: a soledad instance :returns: the port as returned by the reactor when starts listening, and the factory for the protocol. """ leap_check(store, "store cannot be None") # XXX this can also be a ProxiedObject, FIXME # leap_assert_type(store, Soledad) port = kwargs.get('port', IMAP_PORT) userid = kwargs.get('userid', None) leap_check(userid is not None, "need an user id") uuid = store.uuid factory = LeapIMAPFactory(uuid, userid, store) try: tport = reactor.listenTCP(port, factory, interface="localhost") 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. 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,)) emit(catalog.IMAP_SERVICE_STARTED, str(port)) # FIXME -- change service signature return tport, factory # not ok, signal error. emit(catalog.IMAP_SERVICE_FAILED_TO_START, str(port))
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 """ 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,)) emit(catalog.MAIL_FETCHED_INCOMING, str(num_mails), str(fetched_ts)) return doclist
def key_not_found(failure): if not failure.check(KeyNotFound): return failure emit(catalog.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: return failure emit(catalog.KEYMANAGER_LOOKING_FOR_KEY, address) d = self._fetch_keys_from_server(address) d.addCallback(lambda _: self._wrapper_map[ktype].get_key( address, private=False)) d.addCallback(key_found) return d
def key_not_found(failure): if not failure.check(KeyNotFound): return failure emit(catalog.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: return failure emit(catalog.KEYMANAGER_LOOKING_FOR_KEY, address) d = self._fetch_keys_from_server(address) d.addCallback( lambda _: self._wrapper_map[ktype].get_key(address, private=False)) d.addCallback(key_found) return d
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: str :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 outgoing_mail = OutgoingMail(userid, keymanager, smtp_cert, smtp_key, smtp_host, smtp_port) factory = SMTPFactory(userid, keymanager, encrypted_only, outgoing_mail) try: tport = reactor.listenTCP(port, factory, interface="localhost") emit(catalog.SMTP_SERVICE_STARTED, str(port)) return factory, tport except CannotListenError: logger.error("STMP Service failed to start: " "cannot listen in port %s" % port) emit(catalog.SMTP_SERVICE_FAILED_TO_START, str(port)) except Exception as exc: logger.error("Unhandled error while launching smtp gateway service") logger.exception(exc)
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: str :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 outgoing_mail = OutgoingMail( userid, keymanager, smtp_cert, smtp_key, smtp_host, smtp_port) factory = SMTPFactory(userid, keymanager, encrypted_only, outgoing_mail) try: tport = reactor.listenTCP(port, factory, interface="localhost") emit(catalog.SMTP_SERVICE_STARTED, str(port)) return factory, tport except CannotListenError: logger.error("STMP Service failed to start: " "cannot listen in port %s" % port) emit(catalog.SMTP_SERVICE_FAILED_TO_START, str(port)) except Exception as exc: logger.error("Unhandled error while launching smtp gateway service") logger.exception(exc)
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) deferreds = [] for index, doc in enumerate(doclist): logger.debug("processing doc %d of %d" % (index + 1, num_mails)) emit(catalog.MAIL_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...") elif self._is_msg(keys): d = self._decrypt_doc(doc) d.addCallback(self._extract_keys) d.addCallbacks(self._add_message_locally, self._errback) deferreds.append(d) d = defer.gatherResults(deferreds, consumeErrors=True) d.addCallback(lambda _: doclist) return d
def run(self): """ Check for updates """ 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: logger.info("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]) emit(catalog.UPDATER_NEW_UPDATES, ", ".join(filepath)) logger.info("Updates ready: %s" % (filepath,)) return except NotImplemented as e: logger.error("NotImplemented: %s" % (e,)) return except Exception as e: logger.error("An unexpected error has occurred while " "updating: %s" % (e,)) finally: time.sleep(self.delay)
def _sync_soledad(self): """ Synchronize with remote soledad. :returns: a list of LeapDocuments, or None. :rtype: iterable or None """ def _log_synced(result): log.msg('FETCH soledad SYNCED.') return result try: log.msg('FETCH: syncing soledad...') d = self._soledad.sync() d.addCallback(_log_synced) return d # TODO is this still raised? or should we do failure.trap # instead? except InvalidAuthTokenError: # if the token is invalid, send an event so the GUI can # disable mail and show an error message. emit(catalog.SOLEDAD_INVALID_AUTH_TOKEN)
def gen_key(self, ktype): """ Generate a key of type ktype bound to the user's address. :param ktype: The type of the key. :type ktype: subclass of EncryptionKey :return: A Deferred which fires with the generated EncryptionKey. :rtype: Deferred :raise UnsupportedKeyTypeError: if invalid key type """ self._assert_supported_key_type(ktype) def signal_finished(key): emit(catalog.KEYMANAGER_FINISHED_KEY_GENERATION, self._address) return key emit(catalog.KEYMANAGER_STARTED_KEY_GENERATION, self._address) d = self._wrapper_map[ktype].gen_key(self._address) d.addCallback(signal_finished) return d
def _verify_session(self, M2): """ Verifies the session based on the M2 parameter. If the verification succeeds, it sets the session_id for this session Might raise SRPAuthenticationError based: SRPAuthBadDataFromServer SRPAuthVerificationFailed :param M2: M2 SRP parameter :type M2: str """ logger.debug("Verifying session...") try: unhex_M2 = self._safe_unhexlify(M2) except TypeError: logger.error("Bad data from server (HAWK)") raise SRPAuthBadDataFromServer() self._srp_user.verify_session(unhex_M2) if not self._srp_user.authenticated(): logger.error("Auth verification failed.") raise SRPAuthVerificationFailed() logger.debug("Session verified.") session_id = self._session.cookies.get(self.SESSION_ID_KEY, None) if not session_id: logger.error("Bad cookie from server (missing _session_id)") raise SRPAuthNoSessionId() # make the rpc call async emit(catalog.CLIENT_SESSION_ID, session_id) self.set_session_id(session_id) logger.debug("SUCCESS LOGIN") return True
def send(pubkey): 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) emit(catalog.KEYMANAGER_DONE_UPLOADING_KEYS, self._address)
def _maybe_encrypt_and_sign(self, raw, recipient): """ 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 | +---------------------+-------------+---------------+----------------+ :param raw: The raw message :type raw: str :param recipient: The recipient for the message :type: recipient: smtp.User :return: A Deferred that will be fired with a MIMEMultipart message and the original recipient Message :rtype: Deferred """ # pass if the original message's content-type is "multipart/encrypted" origmsg = Parser().parsestr(raw) if origmsg.get_content_type() == 'multipart/encrypted': return defer.success((origmsg, recipient)) from_address = validate_address(self._from_address) username, domain = from_address.split('@') to_address = validate_address(recipient.dest.addrstr) def maybe_encrypt_and_sign(message): d = self._encrypt_and_sign(message, to_address, from_address) d.addCallbacks(signal_encrypt_sign, if_key_not_found_send_unencrypted, errbackArgs=(message,)) return d def signal_encrypt_sign(newmsg): emit(catalog.SMTP_END_ENCRYPT_AND_SIGN, "%s,%s" % (self._from_address, to_address)) return newmsg, recipient def if_key_not_found_send_unencrypted(failure, message): failure.trap(KeyNotFound, KeyAddressMismatch) log.msg('Will send unencrypted message to %s.' % to_address) emit(catalog.SMTP_START_SIGN, self._from_address) d = self._sign(message, from_address) d.addCallback(signal_sign) return d def signal_sign(newmsg): emit(catalog.SMTP_END_SIGN, self._from_address) return newmsg, recipient log.msg("Will encrypt the message with %s and sign with %s." % (to_address, from_address)) emit(catalog.SMTP_START_ENCRYPT_AND_SIGN, "%s,%s" % (self._from_address, to_address)) d = self._maybe_attach_key(origmsg, from_address, to_address) d.addCallback(maybe_encrypt_and_sign) return d
def _signal_unread_to_ui(self, *args): """ Sends unread event to ui. """ emit(catalog.MAIL_UNREAD_MESSAGES, str(self._inbox_collection.count_unseen()))
def signal_finished(key): emit(catalog.KEYMANAGER_FINISHED_KEY_GENERATION, self._address) return key
def we_are_the_one_and_only(): """ Returns True if we are the only instance running, False otherwise. If we came later, send a raise signal to the main instance of the application. Under windows we are not using flock magic, so we wait during RAISE_WINDOW_TIMEOUT time, if not ack is received, we assume it was a stalled lock, so we remove it and continue with initialization. :rtype: bool """ if IS_UNIX: locker = UnixLock() locker.get_lock() we_are_the_one = locker.locked_by_us if not we_are_the_one: emit(catalog.RAISE_WINDOW) return we_are_the_one elif IS_WIN: locker = WindowsLock() locker.get_lock() we_are_the_one = locker.locked_by_us if not we_are_the_one: locker.release_lock() lock_path = locker.get_locking_path() ts = get_modification_ts(lock_path) nowfun = datetime.datetime.now t0 = nowfun() pause = RAISE_WINDOW_TIMEOUT / float(RAISE_WINDOW_WAIT_STEPS) timeout_delta = datetime.timedelta(0, RAISE_WINDOW_TIMEOUT) check_interval = lambda: nowfun() - t0 < timeout_delta # let's assume it's a stalled lock we_are_the_one = True emit(catalog.RAISE_WINDOW) while check_interval(): if get_modification_ts(lock_path) > ts: # yay! someone claimed their control over the lock. # so the lock is alive logger.debug('Raise window ACK-ed') we_are_the_one = False break else: time.sleep(pause) if we_are_the_one: # ok, it really was a stalled lock. let's remove all # that is left, and put only ours there. WindowsLock.release_all_locks() WindowsLock().get_lock() return we_are_the_one else: logger.warning("Multi-instance checker " "not implemented for %s" % (platform.system())) # lies, lies, lies... return True
def found(_): log.msg("Accepting mail for %s..." % user.dest.addrstr) emit(catalog.SMTP_RECIPIENT_ACCEPTED_ENCRYPTED, user.dest.addrstr)
def key_found(key): emit(catalog.KEYMANAGER_KEY_FOUND, address) return key
def signal_sign(newmsg): emit(catalog.SMTP_END_SIGN, self._from_address) return newmsg, recipient
def signal_encrypt_sign(newmsg): emit(catalog.SMTP_END_ENCRYPT_AND_SIGN, "%s,%s" % (self._from_address, to_address)) return newmsg, recipient
def _maybe_encrypt_and_sign(self, raw, recipient): """ 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 | +---------------------+-------------+---------------+----------------+ :param raw: The raw message :type raw: str :param recipient: The recipient for the message :type: recipient: smtp.User :return: A Deferred that will be fired with a MIMEMultipart message and the original recipient Message :rtype: Deferred """ # pass if the original message's content-type is "multipart/encrypted" origmsg = Parser().parsestr(raw) if origmsg.get_content_type() == 'multipart/encrypted': return defer.success((origmsg, recipient)) from_address = validate_address(self._from_address) username, domain = from_address.split('@') to_address = validate_address(recipient.dest.addrstr) def maybe_encrypt_and_sign(message): d = self._encrypt_and_sign(message, to_address, from_address) d.addCallbacks(signal_encrypt_sign, if_key_not_found_send_unencrypted, errbackArgs=(message, )) return d def signal_encrypt_sign(newmsg): emit(catalog.SMTP_END_ENCRYPT_AND_SIGN, "%s,%s" % (self._from_address, to_address)) return newmsg, recipient def if_key_not_found_send_unencrypted(failure, message): failure.trap(KeyNotFound, KeyAddressMismatch) log.msg('Will send unencrypted message to %s.' % to_address) emit(catalog.SMTP_START_SIGN, self._from_address) d = self._sign(message, from_address) d.addCallback(signal_sign) return d def signal_sign(newmsg): emit(catalog.SMTP_END_SIGN, self._from_address) return newmsg, recipient log.msg("Will encrypt the message with %s and sign with %s." % (to_address, from_address)) emit(catalog.SMTP_START_ENCRYPT_AND_SIGN, "%s,%s" % (self._from_address, to_address)) d = self._maybe_attach_key(origmsg, from_address, to_address) d.addCallback(maybe_encrypt_and_sign) return d
def signal_deleted(doc_id): emit(catalog.MAIL_MSG_DELETED_INCOMING) return doc_id