Example #1
0
    def _encrypt_message_with_all(self, encryption_names):
        '''
            Encrypt the message with each encryption program.
        '''
        encrypted = fatal_error = False
        error_message = ''
        encrypted_with = []
        encrypted_classnames = []
        to_user = self.crypto_message.smtp_recipient()
        original_payload = self.crypto_message.get_email_message().get_message().get_payload()

        self._log_message("encrypting using {} with {}'s key".format(encryption_names, to_user))
        for encryption_name in encryption_names:
            encryption_classname = get_key_classname(encryption_name)
            if encryption_classname not in encrypted_classnames:
                try:
                    if options.require_key_verified():
                        __, key_ok, __ = contacts.get_fingerprint(to_user, encryption_name)
                        self._log_message("{} {} key verified: {}".format(to_user, encryption_name, key_ok))
                    else:
                        key_ok = True

                    if key_ok:
                        if self._encrypt_message(encryption_name):
                            encrypted_classnames.append(encryption_classname)
                            encrypted_with.append(encryption_name)
                    else:
                        error_message += i18n('You need to verify the {encryption} key for {email} before you can use it.'.format(
                            encryption=encryption_name, email=to_user))
                        self._log_message(error_message)
                except MessageException as message_exception:
                    fatal_error = True
                    error_message += message_exception.value
                    self._log_exception(error_message)
                    break

        # if the user has encryption software defined, then the message
        # must be encrypted or bounced to the sender
        if len(encrypted_classnames) > 0:
            encrypted = True
        else:
            MSG_NOT_SET = i18n('Message not sent to {email} because there was a problem encrypting.'.format(
                email=to_user))
            fatal_error = True
            if error_message is None or len(error_message) <= 0:
                error_message = '{} {}\n{}'.format(MSG_NOT_SET,
                    i18n("It's possible the recipient's key is missing."),
                    self.POSSIBLE_ENCRYPT_SOLUTION)
            else:
                error_message = '{} {}'.format(MSG_NOT_SET, error_message)

            # restore the payload
            self.crypto_message.get_email_message().get_message().set_payload(original_payload)

        if fatal_error:
            self._log_message('raising message exception in _encrypt_message_with_all')
            self._log_message(error_message)
            raise MessageException(value=error_message)

        return encrypted_with
    def _import_new_key(self, from_user, encryption_name, key_block, id_fingerprint_pairs):
        '''
            Import a new key (internal use only).
        '''

        tag = None
        result_ok = False
        if encryption_name is None:
            encryption_name = ''

        self.new_key_imported = False
        try:
            self.log_message("starting to import new {} key for {}".format(encryption_name, from_user))
            if from_user is None or len(encryption_name) == 0 or id_fingerprint_pairs is None:
                self.log_message('missing key data so unable to import new key')
            else:
                key_crypto = KeyFactory.get_crypto(
                    encryption_name, crypto_software.get_key_classname(encryption_name))

                # make sure that we don't have a key for any of the user ids included with this key
                result_ok = True
                if id_fingerprint_pairs is None or len(id_fingerprint_pairs) <= 0:
                    result_ok = False

                elif len(id_fingerprint_pairs) > 1:
                    for (user_id, __) in id_fingerprint_pairs:
                        crypto_fingerprint, expiration = key_crypto.get_fingerprint(user_id)
                        if crypto_fingerprint is not None:
                            result_ok = False
                            self.log_message('key exists for {} so unable to import key for {}'.format(user_id, from_user))
                            break

                if result_ok:
                    result_ok = key_crypto.import_public(key_block, id_fingerprint_pairs=id_fingerprint_pairs)
                    self.log_message('imported key: {}'.format(result_ok))

                if result_ok:
                    self.new_key_imported = True
                    result_ok = self._add_contacts_and_notify(encryption_name, id_fingerprint_pairs)
                    self.log_message('added contacts: {}'.format(result_ok))

            if not result_ok:
                self.log_message('Unable to import new public key for {}; probably taking longer than expected; check Contacts later'.format(from_user))
        except:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        self.log_message('import new key ok: {}'.format(result_ok))

        return tag
    def _manage_key_header(self, from_user, crypto_message, encryption_name,
                           key_block):
        '''
            Manage a key in the header for the encryption software (internal use only).
        '''

        tag = None

        try:
            if key_block == None or len(key_block.strip()) <= 0:
                self.log_message(
                    "no {} public key in header".format(encryption_name))
            else:
                if self.DEBUGGING:
                    self.log_message("{} key from message:\n{}".format(
                        encryption_name, key_block))

                key_crypto = KeyFactory.get_crypto(
                    encryption_name,
                    crypto_software.get_key_classname(encryption_name))
                if key_crypto is None:
                    id_fingerprint_pairs = None
                    self.log_message(
                        'no key crypto for {}'.format(encryption_name))
                else:
                    id_fingerprint_pairs = key_crypto.get_id_fingerprint_pairs(
                        key_block)

                if id_fingerprint_pairs is None or len(
                        id_fingerprint_pairs) <= 0:
                    tag = None
                    self.log_message('no user keys in key block')
                else:
                    tag = self._manage_public_key(from_user, crypto_message,
                                                  key_crypto, key_block,
                                                  id_fingerprint_pairs)

                if tag is not None and len(tag.strip()) > 0:
                    crypto_message.add_tag_once(tag)

        except MessageException as message_exception:
            raise MessageException(value=message_exception.value)

        except:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        return tag
def prep_sync(contacts_crypto):
    '''
        Prepare to sync database and crypto keys.

        Test extreme case.
        >>> prep_sync(None)
        (False, None, None, None)
    '''

    result_ok = True
    crypto_name = email = key_plugin = None
    try:
        if contacts_crypto is None:
            result_ok = False
            log_message('contacts crypto not defined')
        else:
            crypto_name = contacts_crypto.encryption_software.name
            email = contacts_crypto.contact.email

            log_message('preparing to sync db and {} keyring for {}'.format(
                crypto_name, email))
            crypto_record = crypto_software.get(
                contacts_crypto.encryption_software)
            if crypto_record is None:
                result_ok = False
                log_message('{} encryption not defined in database'.format(
                    crypto_name))
            elif not crypto_record.active:
                result_ok = False
                log_message('{} encryption is not active'.format(crypto_name))
            else:
                key_plugin = KeyFactory.get_crypto(
                    crypto_name,
                    crypto_software.get_key_classname(crypto_name))
                result_ok = key_plugin is not None
                if not result_ok:
                    log_message('key plugin not defined'.format(crypto_name))
    except:
        log_message('{} had an unexpected error'.format(contacts_crypto))
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')
        result_ok = False

    log_message(
        'finished preparing to sync db and keyring for {}'.format(email))

    return result_ok, crypto_name, email, key_plugin
    def _is_ready_for_search(self):
        '''
            Verify that we're ready to search for this key.

            Test extreme case.
            >>> srk_class = SearchKeyserver(None, None, None, None)
            >>> srk_class._is_ready_for_search()
            False
        '''

        ready = False
        try:
            ready = (self.email is not None
                     and self.encryption_name is not None
                     and self.keyserver is not None
                     and self.user_initiated_search is not None
                     and not email_in_domain(self.email))

            if ready:
                self.key_plugin = KeyFactory.get_crypto(
                    self.encryption_name,
                    crypto_software.get_key_classname(self.encryption_name))
                ready = self.key_plugin is not None

            if ready:
                # make sure we don't already have crypto defined for this user
                contacts_crypto = contacts.get_contacts_crypto(
                    self.email, self.encryption_name)
                if contacts_crypto is None or contacts_crypto.fingerprint is None:
                    fingerprint, expiration = self.key_plugin.get_fingerprint(
                        self.email)
                    if fingerprint is not None:
                        ready = False
                        self.log_message(
                            '{} public key exists for {}: {}'.format(
                                self.encryption_name, self.email, fingerprint))
                else:
                    ready = False
                    self.log_message('crypto for {} already defined'.format(
                        self.email))

        except Exception as exception:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')
            ready = False

        return ready
def prep_sync(contacts_crypto):
    '''
        Prepare to sync database and crypto keys.

        Test extreme case.
        >>> prep_sync(None)
        (False, None, None, None)
    '''

    result_ok = True
    crypto_name = email = key_plugin = None
    try:
        if contacts_crypto is None:
            result_ok = False
            log_message('contacts crypto not defined')
        else:
            crypto_name = contacts_crypto.encryption_software.name
            email = contacts_crypto.contact.email

            log_message('preparing to sync db and {} keyring for {}'.format(crypto_name, email))
            crypto_record = crypto_software.get(contacts_crypto.encryption_software)
            if crypto_record is None:
                result_ok = False
                log_message('{} encryption not defined in database'.format(crypto_name))
            elif not crypto_record.active:
                result_ok = False
                log_message('{} encryption is not active'.format(crypto_name))
            else:
                key_plugin = KeyFactory.get_crypto(
                    crypto_name, crypto_software.get_key_classname(crypto_name))
                result_ok = key_plugin is not None
                if not result_ok:
                    log_message('key plugin not defined'.format(crypto_name))
    except:
        log_message('{} had an unexpected error'.format(contacts_crypto))
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')
        result_ok = False

    log_message('finished preparing to sync db and keyring for {}'.format(email))

    return result_ok, crypto_name, email, key_plugin
    def _is_ready_for_search(self):
        '''
            Verify that we're ready to search for this key.

            Test extreme case.
            >>> srk_class = SearchKeyserver(None, None, None, None)
            >>> srk_class._is_ready_for_search()
            False
        '''

        ready = False
        try:
            ready = (self.email is not None and
                     self.encryption_name is not None and
                     self.keyserver is not None and
                     self.user_initiated_search is not None and
                     not email_in_domain(self.email))

            if ready:
                self.key_plugin = KeyFactory.get_crypto(
                   self.encryption_name, crypto_software.get_key_classname(self.encryption_name))
                ready = self.key_plugin is not None

            if ready:
                # make sure we don't already have crypto defined for this user
                contacts_crypto = contacts.get_contacts_crypto(self.email, self.encryption_name)
                if contacts_crypto is None or contacts_crypto.fingerprint is None:
                    fingerprint, expiration = self.key_plugin.get_fingerprint(self.email)
                    if fingerprint is not None:
                        ready = False
                        self.log_message('{} public key exists for {}: {}'.format(
                            self.encryption_name, self.email, fingerprint))
                else:
                    ready = False
                    self.log_message('crypto for {} already defined'.format(self.email))

        except Exception as exception:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')
            ready = False

        return ready
    def _manage_key_header(self, from_user, crypto_message, encryption_name, key_block):
        '''
            Manage a key in the header for the encryption software (internal use only).
        '''

        tag = None

        try:
            if key_block == None or len(key_block.strip()) <= 0:
                self.log_message("no {} public key in header".format(encryption_name))
            else:
                if self.DEBUGGING:
                    self.log_message("{} key from message:\n{}".format(encryption_name, key_block))

                key_crypto = KeyFactory.get_crypto(
                  encryption_name, crypto_software.get_key_classname(encryption_name))
                if key_crypto is None:
                    id_fingerprint_pairs = None
                    self.log_message('no key crypto for {}'.format(encryption_name))
                else:
                    id_fingerprint_pairs = key_crypto.get_id_fingerprint_pairs(key_block)

                if id_fingerprint_pairs is None or len(id_fingerprint_pairs) <= 0:
                    tag = None
                    self.log_message('no user keys in key block')
                else:
                    tag = self._manage_public_key(
                        from_user, crypto_message, key_crypto, key_block, id_fingerprint_pairs)

                if tag is not None and len(tag.strip()) > 0:
                    crypto_message.add_tag_once(tag)

        except MessageException as message_exception:
            raise MessageException(value=message_exception.value)

        except:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        return tag
Example #9
0
    def _clear_sign_message_with_all(self, encryption_names):
        '''
            Clear sign the message with each encryption program.
        '''
        signed = fatal_error = False
        error_message = ''
        signed_with = []
        encrypted_classnames = []

        self._log_message("signing using {} encryption software".format(encryption_names))
        for encryption_name in encryption_names:
            encryption_classname = get_key_classname(encryption_name)
            if encryption_classname not in encrypted_classnames:
                try:
                    if self._clear_sign_message(encryption_name):
                        signed_with.append(encryption_name)
                        encrypted_classnames.append(encryption_classname)
                except MessageException as message_exception:
                    error_message += message_exception.value
                    self._log_exception(error_message)
                    break

        return signed_with
Example #10
0
    def start_retrieval(self):
        '''
            Queue retrieving key from the keyserver. When the job finishes, associated
            database entries will be made from another queued job which is dependent on
            the key retrieval's job.

            Test extreme case.
            >>> rk = RetrieveKey(None, None, None, None, None)
            >>> rk.start_retrieval()
            False
        '''

        if self.email == UNKNOWN_EMAIL:
            email_or_fingerprint = self.key_id
        else:
            email_or_fingerprint = self.email

        try:
            result_ok = (self.email is not None and
                         self.encryption_name is not None and
                         self.keyserver is not None and
                         self.key_id is not None and
                         self.user_initiated_search is not None)

            if result_ok:
                self.key_plugin = KeyFactory.get_crypto(
                   self.encryption_name, crypto_software.get_key_classname(self.encryption_name))
                result_ok = self.key_plugin is not None

            if result_ok:
                from goodcrypto.mail.keyserver_utils import add_contact_records

                self.key_plugin.retrieve_key(self.key_id, self.keyserver)

                retrieve_job = self.key_plugin.get_job()
                queue = self.key_plugin.get_queue()
                if queue is None or retrieve_job is None:
                    self.log_message('unable to queue job to add contact recs for {}'.format(
                       self.key_id))
                    result_ok = False
                else:
                    self.log_message('starting to add contact records for {} (after job: {})'.format(
                        self.key_id, retrieve_job.get_id()))
                    add_contact_records(email_or_fingerprint,
                                        self.encryption_name,
                                        self.user_initiated_search,
                                        retrieve_job.get_id(), queue.key)
                    result_ok = True
            else:
                result_ok = False
                self.log_message('unable to queue retrieving {} key for {}'.format(
                    self.encryption_name, self.key_id))

        except Exception as exception:
            result_ok = False
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        self.log_message('finished queueing retreival for {} ok: {}'.format(email_or_fingerprint, result_ok))

        return result_ok
Example #11
0
def add_contact_records(email_or_fingerprint, crypto_name,
                        user_initiated_search, job_id, queue_key):
    '''
        Add contact and associated crypto records in database.

        This function cannot be part of a class because it's
        passed to RQ which only accepts standalone functions, not
        functions in an instance of a class.  Also, the function must
        be defined before the class which calls it or located in a separate
        file and imported.

        Test extreme case.
        >>> add_contact_records(None, None, None, None, None)
        False
    '''
    result_ok = timed_out = False
    log_message('entered add_contact_records')

    try:
        __, email = parse_address(email_or_fingerprint)
        if email is None:
            key_id = email_or_fingerprint
        else:
            key_id = None

        log_message(
            'adding a {} contact for {} if key retrieved by {} job'.format(
                crypto_name, email_or_fingerprint, job_id))
        key_plugin = KeyFactory.get_crypto(
            crypto_name, crypto_software.get_key_classname(crypto_name))

        if queue_key is None or job_id is None:
            result_ok = False
        else:
            MAX_WAIT = 10 * 60  # seconds

            queue = Queue.from_queue_key(
                queue_key, connection=key_plugin.get_queue_connection())
            job = queue.fetch_job(job_id)

            # if this function is added to a queue (RQ), then it never starts
            # so we're going to do this the old fashion way
            waited = 0
            while not job.is_finished and not job.is_failed and waited < MAX_WAIT:
                sleep(1)

            if job.is_failed:
                log_message('retrieving {} key for {} job failed'.format(
                    crypto_name, email_or_fingerprint))
                result_ok = False
            else:
                # even if the job timed out, see if the key was retrieved
                result_ok, timed_out, output, error = key_plugin.get_background_job_results(
                    email_or_fingerprint, job)
                log_message(
                    "results from retrieving {} key for {}, result ok: {}; timed out: {}"
                    .format(crypto_name, email_or_fingerprint, result_ok,
                            timed_out))
                if result_ok:
                    imported_user_ids = key_plugin.parse_keyserver_ids_retrieved(
                        error)
                    log_message(
                        "imported user ids: {}".format(imported_user_ids))
                    if len(imported_user_ids) < 1 and error:
                        log_message('error: {}'.format(error))
                if output: log_message('output: {}'.format(output))

        if result_ok:
            id_fingerprint_pairs = []
            for user_id, imported_key_id in imported_user_ids:
                contact = contacts.add(user_id, crypto_name, source=KEYSERVER)
                result_ok = contact is not None
                log_message("added contact's crypto for {}: {}".format(
                    user_id, result_ok))

                if result_ok:
                    # change the outgoing policy if needed
                    if contact.outbound_encrypt_policy != DEFAULT_OUTBOUND_ENCRYPT_POLICY:
                        contact.outbound_encrypt_policy = DEFAULT_OUTBOUND_ENCRYPT_POLICY
                        contact.save()

                    # activate the contact's crypto "after save signal" to update the fingerprint
                    contacts_crypto = contacts.get_contacts_crypto(
                        user_id, crypto_name)
                    if contacts_crypto is not None:
                        if contacts_crypto.fingerprint is None:
                            contacts_crypto.source = KEYSERVER
                            contacts_crypto.save()
                            if key_id is None:
                                fingerprint = imported_key_id
                            else:
                                fingerprint = key_id
                        else:
                            fingerprint = contacts_crypto.fingerprint
                    elif key_id is None:
                        fingerprint = imported_key_id
                    else:
                        fingerprint = key_id

                    id_fingerprint_pairs.append(
                        (contact.email, format_fingerprint(fingerprint)))
                else:
                    log_message(
                        'unable to add {} contact record for {}'.format(
                            crypto_name, user_id))

            if result_ok and len(id_fingerprint_pairs) > 0:
                log_message('notifying {} about new keys: {}'.format(
                    user_initiated_search, id_fingerprint_pairs))
                notify_new_key_arrived(user_initiated_search,
                                       id_fingerprint_pairs)

        elif timed_out:
            log_message('timed out retrieving a {} key for {}.'.format(
                crypto_name, email_or_fingerprint))
        else:
            log_message('unable to retrieve {} key for {}.'.format(
                crypto_name, email_or_fingerprint))

    except Exception as exception:
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')
        result_ok = False

    log_message('ended add_contact_records: {}'.format(result_ok))

    return result_ok
Example #12
0
    def keys_in_header(self, crypto_message):
        '''
            Return true if there are public keys in the message's header.
        '''
        header_contains_key_info = False
        try:
            good_key = True
            from_user = crypto_message.smtp_sender()

            accepted_crypto_packages = crypto_message.get_accepted_crypto_software()
            if accepted_crypto_packages is not None and len(accepted_crypto_packages) > 0:
                self.log_message("checking for {} keys".format(accepted_crypto_packages))
                for encryption_name in accepted_crypto_packages:
                    # see if there's a the key block for this encryption program
                    header_name = get_public_key_header_name(encryption_name)
                    key_block = get_multientry_header(
                      crypto_message.get_email_message().get_message(), header_name)
                    # see if there's a plain key block
                    if ((key_block is None or len(key_block) <= 0) and
                        len(accepted_crypto_packages) == 1):
                        self.log_message("no {} public key in header so trying generic header".format(encryption_name))
                        key_block = get_multientry_header(
                          crypto_message.get_email_message().get_message(), PUBLIC_KEY_HEADER)

                    if key_block is not None and len(key_block) > 0:
                        header_contains_key_info = True

                        user_ids = []
                        key_crypto = KeyFactory.get_crypto(
                          encryption_name, crypto_software.get_key_classname(encryption_name))
                        if key_crypto is None:
                            id_fingerprint_pairs = None
                            self.log_message('no key crypto for {}'.format(encryption_name))
                        else:
                            id_fingerprint_pairs = key_crypto.get_id_fingerprint_pairs(key_block)
                            for (user_id, __) in id_fingerprint_pairs:
                                user_ids.append(user_id)
                            self.log_message("key block includes key for {}".format(user_ids))

                        # don't consider a key valid if it's not from the sender
                        if from_user not in user_ids:
                            tag = notices.report_bad_header_key(
                                self.recipient_to_notify, from_user, user_ids,
                                encryption_name, crypto_message)
                            self.log_message('tag: {}'.format(tag))

                            if crypto_message.get_email_message().is_probably_pgp():
                                crypto_message.drop(dropped=True)
                                self.log_message('serious error in header so original message sent as attchment')
                                raise MessageException(value=i18n(tag))

                self.update_accepted_crypto(from_user, accepted_crypto_packages)

        except MessageException as message_exception:
            raise MessageException(value=message_exception.value)

        except:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')
            if crypto_message is not None:
                crypto_message.add_error_tag_once(self.UNEXPECTED_ERROR)

        return header_contains_key_info
    def _import_new_key(self, from_user, encryption_name, key_block,
                        id_fingerprint_pairs):
        '''
            Import a new key (internal use only).
        '''

        tag = None
        result_ok = False
        if encryption_name is None:
            encryption_name = ''

        self.new_key_imported = False
        try:
            self.log_message("starting to import new {} key for {}".format(
                encryption_name, from_user))
            if from_user is None or len(
                    encryption_name) == 0 or id_fingerprint_pairs is None:
                self.log_message(
                    'missing key data so unable to import new key')
            else:
                key_crypto = KeyFactory.get_crypto(
                    encryption_name,
                    crypto_software.get_key_classname(encryption_name))

                # make sure that we don't have a key for any of the user ids included with this key
                result_ok = True
                if id_fingerprint_pairs is None or len(
                        id_fingerprint_pairs) <= 0:
                    result_ok = False

                elif len(id_fingerprint_pairs) > 1:
                    for (user_id, __) in id_fingerprint_pairs:
                        crypto_fingerprint, expiration = key_crypto.get_fingerprint(
                            user_id)
                        if crypto_fingerprint is not None:
                            result_ok = False
                            self.log_message(
                                'key exists for {} so unable to import key for {}'
                                .format(user_id, from_user))
                            break

                if result_ok:
                    result_ok = key_crypto.import_public(
                        key_block, id_fingerprint_pairs=id_fingerprint_pairs)
                    self.log_message('imported key: {}'.format(result_ok))

                if result_ok:
                    self.new_key_imported = True
                    result_ok = self._add_contacts_and_notify(
                        encryption_name, id_fingerprint_pairs)
                    self.log_message('added contacts: {}'.format(result_ok))

            if not result_ok:
                self.log_message(
                    'Unable to import new public key for {}; probably taking longer than expected; check Contacts later'
                    .format(from_user))
        except:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')

        self.log_message('import new key ok: {}'.format(result_ok))

        return tag
Example #14
0
def get_key(email, crypto_name, keyserver, user_initiated_search, job_id,
            queue_key):
    '''
        Get a key from the keyserver.

        This function cannot be part of a class because it's
        passed to RQ which only accepts standalone functions, not
        functions in an instance of a class. Also, the function must
        be located in a separate file and imported or defined before
        the class which calls it.

        # Test extreme case
        >>> get_key(None, None, None, None, None, None)
        False
    '''

    GOOD_CONNECTION = i18n('Good connection')

    result_ok = timed_out = False
    output = error = None

    try:
        log_message('starting to get_key for {}'.format(email))

        key_plugin = KeyFactory.get_crypto(
            crypto_name, crypto_software.get_key_classname(crypto_name))
        if job_id is None or queue_key is None:
            result_ok = False
        else:
            MAX_WAIT = 5 * 60  # seconds

            log_message(
                'checking queue for results of searching for a {} key for {}'.
                format(crypto_name, email))

            queue = Queue.from_queue_key(
                queue_key, connection=key_plugin.get_queue_connection())
            search_job = queue.fetch_job(job_id)

            # if this function is added to a queue (RQ), then it never starts
            # so we're going to do this the old fashion way
            waited = 0
            while not search_job.is_finished and not search_job.is_failed and waited < MAX_WAIT:
                sleep(1)

            if search_job.is_finished:
                result_ok, timed_out, output, error = key_plugin.get_background_job_results(
                    email,
                    search_job,
                    good_result=key_plugin.get_good_search_result())

                log_message(
                    'results from searching for {} key for {}, result ok: {}; timed out: {}'
                    .format(crypto_name, email, result_ok, timed_out))
                if output: log_message(output)
                if error: log_message(error)

            elif search_job.is_failed:
                log_message('searching for {} key for {} job failed'.format(
                    crypto_name, email))
                result_ok = False

            else:
                log_message(
                    'searching for {} key for {} job status: {}'.format(
                        crypto_name, email, search_job.get_status()))
                result_ok = False

        if result_ok:
            key_id = key_plugin.parse_keyserver_search(output)
            if key_id is None:
                result_ok = False
                error_message = key_plugin.parse_keyserver_search_error(
                    output, error)
                if error_message is None:
                    # if we didn't find the key, but we connected ok, then
                    # we just want to keep track of the good connection
                    update_last_access(keyserver, crypto_name, date.today(),
                                       GOOD_CONNECTION)
                else:
                    update_last_access(keyserver, crypto_name, date.today(),
                                       error_message)
            else:
                log_message(
                    'starting to retrieve {} key for {} ({}) from {}'.format(
                        crypto_name, email, key_id, keyserver))

                update_last_access(keyserver, crypto_name, date.today(),
                                   GOOD_CONNECTION)

                rk = RetrieveKey(email, crypto_name, keyserver, key_id,
                                 user_initiated_search)
                if rk:
                    result_ok = rk.start_retrieval()
                    log_message(
                        'started retrieval from {}; result for {} ok: {}'.
                        format(keyserver, email, result_ok))
                else:
                    result_ok = False

    except Exception as exception:
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')
        result_ok = False
    finally:
        log_message('finished get_key for {}: {}'.format(email, result_ok))

    return result_ok
    def keys_in_header(self, crypto_message):
        '''
            Return true if there are public keys in the message's header.
        '''
        header_contains_key_info = False
        try:
            good_key = True
            from_user = crypto_message.smtp_sender()

            accepted_crypto_packages = crypto_message.get_accepted_crypto_software(
            )
            if accepted_crypto_packages is not None and len(
                    accepted_crypto_packages) > 0:
                self.log_message(
                    "checking for {} keys".format(accepted_crypto_packages))
                for encryption_name in accepted_crypto_packages:
                    # see if there's a the key block for this encryption program
                    header_name = get_public_key_header_name(encryption_name)
                    key_block = get_multientry_header(
                        crypto_message.get_email_message().get_message(),
                        header_name)
                    # see if there's a plain key block
                    if ((key_block is None or len(key_block) <= 0)
                            and len(accepted_crypto_packages) == 1):
                        self.log_message(
                            "no {} public key in header so trying generic header"
                            .format(encryption_name))
                        key_block = get_multientry_header(
                            crypto_message.get_email_message().get_message(),
                            PUBLIC_KEY_HEADER)

                    if key_block is not None and len(key_block) > 0:
                        header_contains_key_info = True

                        user_ids = []
                        key_crypto = KeyFactory.get_crypto(
                            encryption_name,
                            crypto_software.get_key_classname(encryption_name))
                        if key_crypto is None:
                            id_fingerprint_pairs = None
                            self.log_message(
                                'no key crypto for {}'.format(encryption_name))
                        else:
                            id_fingerprint_pairs = key_crypto.get_id_fingerprint_pairs(
                                key_block)
                            for (user_id, __) in id_fingerprint_pairs:
                                user_ids.append(user_id)
                            self.log_message(
                                "key block includes key for {}".format(
                                    user_ids))

                        # don't consider a key valid if it's not from the sender
                        if from_user not in user_ids:
                            tag = notices.report_bad_header_key(
                                self.recipient_to_notify, from_user, user_ids,
                                encryption_name, crypto_message)
                            self.log_message('tag: {}'.format(tag))

                            if crypto_message.get_email_message(
                            ).is_probably_pgp():
                                crypto_message.drop(dropped=True)
                                self.log_message(
                                    'serious error in header so original message sent as attchment'
                                )
                                raise MessageException(value=i18n(tag))

                self.update_accepted_crypto(from_user,
                                            accepted_crypto_packages)

        except MessageException as message_exception:
            raise MessageException(value=message_exception.value)

        except:
            record_exception()
            self.log_message('EXCEPTION - see syr.exception.log for details')
            if crypto_message is not None:
                crypto_message.add_error_tag_once(self.UNEXPECTED_ERROR)

        return header_contains_key_info
def get_key(email, crypto_name, keyserver, user_initiated_search, job_id, queue_key):
    '''
        Get a key from the keyserver.

        This function cannot be part of a class because it's
        passed to RQ which only accepts standalone functions, not
        functions in an instance of a class. Also, the function must
        be located in a separate file and imported or defined before
        the class which calls it.

        # Test extreme case
        >>> get_key(None, None, None, None, None, None)
        False
    '''

    GOOD_CONNECTION = i18n('Good connection')

    result_ok = timed_out = False
    output = error = None

    try:
        log_message('starting to get_key for {}'.format(email))

        key_plugin = KeyFactory.get_crypto(crypto_name, crypto_software.get_key_classname(crypto_name))
        if job_id is None or queue_key is None:
            result_ok = False
        else:
            MAX_WAIT = 5 * 60 # seconds

            log_message('checking queue for results of searching for a {} key for {}'.format(
                crypto_name, email))

            queue = Queue.from_queue_key(queue_key, connection=key_plugin.get_queue_connection())
            search_job = queue.fetch_job(job_id)

            # if this function is added to a queue (RQ), then it never starts
            # so we're going to do this the old fashion way
            waited = 0
            while not search_job.is_finished and not search_job.is_failed and waited < MAX_WAIT:
                sleep(1)

            if search_job.is_finished:
                result_ok, timed_out, output, error = key_plugin.get_background_job_results(
                    email, search_job, good_result=key_plugin.get_good_search_result())

                log_message('results from searching for {} key for {}, result ok: {}; timed out: {}'.format(
                    crypto_name, email, result_ok, timed_out))
                if output: log_message(output)
                if error: log_message(error)

            elif search_job.is_failed:
                log_message('searching for {} key for {} job failed'.format(crypto_name, email))
                result_ok = False

            else:
                log_message('searching for {} key for {} job status: {}'.format(
                    crypto_name, email, search_job.get_status()))
                result_ok = False

        if result_ok:
            key_id = key_plugin.parse_keyserver_search(output)
            if key_id is None:
                result_ok = False
                error_message = key_plugin.parse_keyserver_search_error(output, error)
                if error_message is None:
                    # if we didn't find the key, but we connected ok, then
                    # we just want to keep track of the good connection
                    update_last_access(keyserver, crypto_name, date.today(), GOOD_CONNECTION)
                else:
                    update_last_access(keyserver, crypto_name, date.today(), error_message)
            else:
                log_message('starting to retrieve {} key for {} ({}) from {}'.format(crypto_name, email, key_id, keyserver))

                update_last_access(keyserver, crypto_name, date.today(), GOOD_CONNECTION)

                rk= RetrieveKey(email, crypto_name, keyserver, key_id, user_initiated_search)
                if rk:
                    result_ok = rk.start_retrieval()
                    log_message('started retrieval from {}; result for {} ok: {}'.format(keyserver, email, result_ok))
                else:
                    result_ok = False

    except Exception as exception:
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')
        result_ok = False
    finally:
        log_message('finished get_key for {}: {}'.format(email, result_ok))

    return result_ok
def add_contact_records(email_or_fingerprint, crypto_name, user_initiated_search, job_id, queue_key):
    '''
        Add contact and associated crypto records in database.

        This function cannot be part of a class because it's
        passed to RQ which only accepts standalone functions, not
        functions in an instance of a class.  Also, the function must
        be defined before the class which calls it or located in a separate
        file and imported.

        Test extreme case.
        >>> add_contact_records(None, None, None, None, None)
        False
    '''
    result_ok = timed_out = False
    log_message('entered add_contact_records')

    try:
        __, email = parse_address(email_or_fingerprint)
        if email is None:
            key_id = email_or_fingerprint
        else:
            key_id = None

        log_message('adding a {} contact for {} if key retrieved by {} job'.format(
            crypto_name, email_or_fingerprint, job_id))
        key_plugin = KeyFactory.get_crypto(
          crypto_name, crypto_software.get_key_classname(crypto_name))

        if queue_key is None or job_id is None:
            result_ok = False
        else:
            MAX_WAIT = 10 * 60 # seconds

            queue = Queue.from_queue_key(queue_key, connection=key_plugin.get_queue_connection())
            job = queue.fetch_job(job_id)

            # if this function is added to a queue (RQ), then it never starts
            # so we're going to do this the old fashion way
            waited = 0
            while not job.is_finished and not job.is_failed and waited < MAX_WAIT:
                sleep(1)

            if job.is_failed:
                log_message('retrieving {} key for {} job failed'.format(crypto_name, email_or_fingerprint))
                result_ok = False
            else:
                # even if the job timed out, see if the key was retrieved
                result_ok, timed_out, output, error = key_plugin.get_background_job_results(
                   email_or_fingerprint, job)
                log_message("results from retrieving {} key for {}, result ok: {}; timed out: {}".format(
                    crypto_name, email_or_fingerprint, result_ok, timed_out))
                if result_ok:
                    imported_user_ids = key_plugin.parse_keyserver_ids_retrieved(error)
                    log_message("imported user ids: {}".format(imported_user_ids))
                    if len(imported_user_ids) < 1 and error:
                        log_message('error: {}'.format(error))
                if output: log_message('output: {}'.format(output))

        if result_ok:
            id_fingerprint_pairs = []
            for user_id, imported_key_id in imported_user_ids:
                contact = contacts.add(user_id, crypto_name, source=KEYSERVER)
                result_ok = contact is not None
                log_message("added contact's crypto for {}: {}".format(user_id, result_ok))

                if result_ok:
                    # change the outgoing policy if needed
                    if contact.outbound_encrypt_policy != DEFAULT_OUTBOUND_ENCRYPT_POLICY:
                        contact.outbound_encrypt_policy = DEFAULT_OUTBOUND_ENCRYPT_POLICY
                        contact.save()

                    # activate the contact's crypto "after save signal" to update the fingerprint
                    contacts_crypto = contacts.get_contacts_crypto(user_id, crypto_name)
                    if contacts_crypto is not None:
                        if contacts_crypto.fingerprint is None:
                            contacts_crypto.source = KEYSERVER
                            contacts_crypto.save()
                            if key_id is None:
                                fingerprint = imported_key_id
                            else:
                                fingerprint = key_id
                        else:
                            fingerprint = contacts_crypto.fingerprint
                    elif key_id is None:
                        fingerprint = imported_key_id
                    else:
                        fingerprint = key_id

                    id_fingerprint_pairs.append((contact.email, format_fingerprint(fingerprint)))
                else:
                    log_message('unable to add {} contact record for {}'.format(crypto_name, user_id))

            if result_ok and len(id_fingerprint_pairs) > 0:
                log_message('notifying {} about new keys: {}'.format(user_initiated_search, id_fingerprint_pairs))
                notify_new_key_arrived(user_initiated_search, id_fingerprint_pairs)

        elif timed_out:
            log_message('timed out retrieving a {} key for {}.'.format(
                crypto_name, email_or_fingerprint))
        else:
            log_message('unable to retrieve {} key for {}.'.format(
                crypto_name, email_or_fingerprint))

    except Exception as exception:
        record_exception()
        log_message('EXCEPTION - see syr.exception.log for details')
        result_ok = False

    log_message('ended add_contact_records: {}'.format(result_ok))

    return result_ok