def func_wrapper(stanza):
            stanza = ensure_unicode_stanza(stanza)

            recipient = xmpp.get_root_attrib(stanza, attrib)
            try:
                contat_jid = recipient.rsplit('/', 1)[0]
            except AttributeError:
                log.error('Recipient not valid.')
                return else_return

            log.info('Checking Sessions for {0}'.format(recipient))
            state = ProfOmemoState()
            uninitialized_devices = state.devices_without_sessions(contat_jid)

            own_jid = ProfOmemoUser.account
            own_uninitialized = state.devices_without_sessions(own_jid)

            uninitialized_devices += own_uninitialized

            if not uninitialized_devices:
                log.info('Recipient {0} has all sessions set up.'.format(recipient))
                return func(stanza)

            _query_device_list(contat_jid)
            _query_device_list(own_jid)
            log.warning('No Session found for user: {0}.'.format(recipient))
            prof.notify('Failed to send last Message.', 5000, 'Profanity Omemo Plugin')
            return else_return
Beispiel #2
0
        def func_wrapper(stanza):
            stanza = ensure_unicode_stanza(stanza)

            recipient = xmpp.get_root_attrib(stanza, attrib)
            try:
                contat_jid = recipient.rsplit('/', 1)[0]
            except AttributeError:
                log.error('Recipient not valid.')
                return else_return

            log.info('Checking Sessions for {0}'.format(recipient))
            state = ProfOmemoState()
            uninitialized_devices = state.devices_without_sessions(contat_jid)

            own_jid = ProfOmemoUser.account
            own_uninitialized = state.devices_without_sessions(own_jid)

            uninitialized_devices += own_uninitialized

            if not uninitialized_devices:
                log.info(
                    'Recipient {0} has all sessions set up.'.format(recipient))
                return func(stanza)

            _query_device_list(contat_jid)
            _query_device_list(own_jid)
            log.warning('No Session found for user: {0}.'.format(recipient))
            prof.notify('Failed to send last Message.', 5000,
                        'Profanity Omemo Plugin')
            return else_return
    def test_omemo_state_returns_singleton(self, mockdb):
        mockdb.return_value = get_test_db_connection()
        account = '*****@*****.**'
        fulljid = '[email protected]/profanity'

        ProfOmemoUser.set_user(account, fulljid)

        state = ProfOmemoState()

        new_state = ProfOmemoState()

        assert state == new_state
def update_devicelist(from_jid, recipient, devices):
    omemo_state = ProfOmemoState()

    logger.debug('Update devices for account: {0}'.format(from_jid))
    logger.info('Adding Device ID\'s: {0} for {1}.'.format(devices, recipient))
    if devices:
        if from_jid == recipient:
            logger.info('Adding own devices')
            omemo_state.set_own_devices(devices)
        else:
            logger.info('Adding recipients devices')
            omemo_state.set_devices(recipient, devices)

        omemo_state.store.sessionStore.setActiveState(devices, from_jid)
        logger.info('Device List update done.')
Beispiel #5
0
def create_devicelist_update_msg(fulljid):
    logger.debug('Create devicelist update message for jid {0}.'.format(fulljid))
    QUERY_MSG = ('<iq type="set" from="{from}" id="{id}">'
                 '<pubsub xmlns="http://jabber.org/protocol/pubsub">'
                 '<publish node="{devicelist_ns}">'
                 '<item id="1">'
                 '<list xmlns="{omemo_ns}">'
                 '{devices}'
                 '</list>'
                 '</item>'
                 '</publish>'
                 '</pubsub>'
                 '</iq>')

    omemo_state = ProfOmemoState()

    own_devices = set(omemo_state.own_devices + [omemo_state.own_device_id])
    logger.debug('Found own devices {0}'.format(own_devices))
    device_nodes = ['<device id="{0}"/>'.format(d) for d in own_devices]

    msg_dict = {'from': fulljid,
                'devices': ''.join(device_nodes),
                'id': str(uuid.uuid4()),
                'omemo_ns': NS_OMEMO,
                'devicelist_ns': NS_DEVICE_LIST}

    query_msg = QUERY_MSG.format(**msg_dict)

    return query_msg
    def test_omemo_state_raises_runtime_error_if_not_connected(self, mockdb):
        mockdb.return_value = get_test_db_connection()

        ProfOmemoUser.reset()

        with pytest.raises(RuntimeError):
            _ = ProfOmemoState()
    def test_encrypt_stanza(self, mockdb):
        pytest.skip('Work in Progress')
        mockdb.return_value = get_test_db_connection()

        current_user = '******'
        ProfOmemoUser.set_user(current_user, current_user + '/profanity')

        recipient = '*****@*****.**'
        omemo_state = ProfOmemoState()
        omemo_state.set_devices(recipient, [4711, 995746])

        raw_stanza = ('<message id="msg0x001" to="{0}" type="chat">'
                      '<body>Hollo</body>'
                      '<active xmlns="http://jabber.org/protocol/chatstates"/>'
                      '</message>').format(recipient)

        encrypted = xmpp.encrypt_stanza(raw_stanza)
Beispiel #8
0
def create_encrypted_message(from_jid, to_jid, plaintext, msg_id=None):

    OMEMO_MSG = ('<message to="{to}" from="{from}" id="{id}" type="chat">'
                 '<body>I sent you an OMEMO encrypted message.</body>'
                 '<encrypted xmlns="{omemo_ns}">'
                 '<header sid="{sid}">'
                 '{keys}'
                 '<iv>{iv}</iv>'
                 '</header>'
                 '<payload>{enc_body}</payload>'
                 '</encrypted>'
                 '<markable xmlns="urn:xmpp:chat-markers:0"/>'
                 '<request xmlns="urn:xmpp:receipts"/>'
                 '<store xmlns="urn:xmpp:hints"/>'
                 '</message>')

    omemo_state = ProfOmemoState()
    account = ProfOmemoUser.account
    msg_data = omemo_state.create_msg(account, to_jid, plaintext)

    # build encrypted message from here
    keys_dict = msg_data['keys'] or {}

    # key is now a tuple of (key, is_prekey)
    keys_str = ''
    for rid, key_info in keys_dict.items():
        key, is_prekey = key_info
        if is_prekey:
            logger.debug('PreKey=True')
            tpl = '<key prekey="true" rid="{0}">{1}</key>'
        else:
            tpl = '<key rid="{0}">{1}</key>'
        keys_str += tpl.format(rid, b64encode(key))

    msg_dict = {'to': to_jid,
                'from': from_jid,
                'id': msg_id or str(uuid.uuid4()),
                'omemo_ns': NS_OMEMO,
                'sid': msg_data['sid'],
                'keys': keys_str,
                'iv': b64encode(msg_data['iv']),
                'enc_body': b64encode(msg_data['payload'])}

    enc_msg = OMEMO_MSG.format(**msg_dict)

    return enc_msg
def create_encrypted_message(from_jid, to_jid, plaintext, msg_id=None):

    OMEMO_MSG = ('<message to="{to}" from="{from}" id="{id}" type="chat">'
                 '<body>I sent you an OMEMO encrypted message.</body>'
                 '<encrypted xmlns="{omemo_ns}">'
                 '<header sid="{sid}">'
                 '{keys}'
                 '<iv>{iv}</iv>'
                 '</header>'
                 '<payload>{enc_body}</payload>'
                 '</encrypted>'
                 '<markable xmlns="urn:xmpp:chat-markers:0"/>'
                 '<request xmlns="urn:xmpp:receipts"/>'
                 '<store xmlns="urn:xmpp:hints"/>'
                 '</message>')

    omemo_state = ProfOmemoState()
    account = ProfOmemoUser.account
    msg_data = omemo_state.create_msg(account, to_jid, plaintext)

    # build encrypted message from here
    keys_dict = msg_data['keys'] or {}

    # key is now a tuple of (key, is_prekey)
    keys_str = ''
    for rid, key_info in keys_dict.items():
        key, is_prekey = key_info
        if is_prekey:
            logger.debug('PreKey=True')
            tpl = '<key prekey="true" rid="{0}">{1}</key>'
        else:
            tpl = '<key rid="{0}">{1}</key>'
        keys_str += tpl.format(rid, b64encode(key).decode('ascii'))

    msg_dict = {'to': to_jid,
                'from': from_jid,
                'id': msg_id or str(uuid.uuid4()),
                'omemo_ns': NS_OMEMO,
                'sid': msg_data['sid'],
                'keys': keys_str,
                'iv': b64encode(msg_data['iv']).decode('ascii'),
                'enc_body': b64encode(msg_data['payload']).decode('ascii')}

    enc_msg = OMEMO_MSG.format(**msg_dict)

    return enc_msg
    def test_encrypt_stanza(self, mockdb):
        pytest.skip('Work in Progress')
        mockdb.return_value = get_test_db_connection()

        current_user = '******'
        ProfOmemoUser.set_user(current_user, current_user + '/profanity')

        recipient = '*****@*****.**'
        omemo_state = ProfOmemoState()
        omemo_state.set_devices(recipient, [4711, 995746])

        raw_stanza = ('<message id="msg0x001" to="{0}" type="chat">'
                        '<body>Hollo</body>'
                        '<active xmlns="http://jabber.org/protocol/chatstates"/>'
                      '</message>'
                      ).format(recipient)

        encrypted = xmpp.encrypt_stanza(raw_stanza)
Beispiel #11
0
def prof_pre_chat_message_send(barejid, message):
    """ Called before a chat message is sent

    :returns: the new message to send, returning None stops the message
              from being sent
    """

    plugin_enabled = _get_omemo_enabled_setting()
    if not plugin_enabled:
        return message

    if not ProfActiveOmemoChats.account_is_active(barejid):
        log.info('Chat not activated for {0}'.format(barejid))
        return message

    omemo_state = ProfOmemoState()
    uninitialzed_devices = omemo_state.devices_without_sessions(barejid)

    if uninitialzed_devices:
        d_str = ', '.join([str(d) for d in uninitialzed_devices])
        msg = 'Requesting bundles for missing devices {0}'.format(d_str)

        log.info(msg)
        prof.notify(msg, 5000, 'Profanity Omemo Plugin')

        for device in uninitialzed_devices:
            _query_bundle_info_for(barejid, device)

    own_jid = ProfOmemoUser.account
    own_uninitialized = omemo_state.devices_without_sessions(own_jid)

    if own_uninitialized:
        d_str = ', '.join([str(d) for d in own_uninitialized])
        msg = 'Requesting own bundles for missing devices {0}'.format(d_str)

        log.info(msg)
        prof.notify(msg, 5000, 'Profanity Omemo Plugin')

        for device in own_uninitialized:
            _query_bundle_info_for(own_jid, device)

    return message
def prof_pre_chat_message_send(barejid, message):
    """ Called before a chat message is sent

    :returns: the new message to send, returning None stops the message
              from being sent
    """

    plugin_enabled = _get_omemo_enabled_setting()
    if not plugin_enabled:
        return message

    if not ProfActiveOmemoChats.account_is_active(barejid):
        log.info('Chat not activated for {0}'.format(barejid))
        return message

    omemo_state = ProfOmemoState()
    uninitialzed_devices = omemo_state.devices_without_sessions(barejid)

    if uninitialzed_devices:
        d_str = ', '.join([str(d) for d in uninitialzed_devices])
        msg = 'Requesting bundles for missing devices {0}'.format(d_str)

        log.info(msg)
        prof.notify(msg, 5000, 'Profanity Omemo Plugin')

        for device in uninitialzed_devices:
            _query_bundle_info_for(barejid, device)

    own_jid = ProfOmemoUser.account
    own_uninitialized = omemo_state.devices_without_sessions(own_jid)

    if own_uninitialized:
        d_str = ', '.join([str(d) for d in own_uninitialized])
        msg = 'Requesting own bundles for missing devices {0}'.format(d_str)

        log.info(msg)
        prof.notify(msg, 5000, 'Profanity Omemo Plugin')

        for device in own_uninitialized:
            _query_bundle_info_for(own_jid, device)

    return message
Beispiel #13
0
def _handle_bundle_update(stanza):
    log.info('Bundle Information received.')
    omemo_state = ProfOmemoState()
    bundle_info = xmpp.unpack_bundle_info(stanza)

    if not bundle_info:
        log.error('Could not unpack bundle info.')
        return

    sender = bundle_info.get('sender')
    device_id = bundle_info.get('device')

    try:
        omemo_state.build_session(sender, device_id, bundle_info)
        log.info('Session built with user: {0}:{1}'.format(sender, device_id))
        prof.completer_add('/omemo end', [sender])
    except Exception as e:
        msg_tmpl = 'Could not build session with {0}:{1}. {2}:{3}'
        msg = msg_tmpl.format(sender, device_id, type(e).__name__, str(e))
        log.error(msg)
        return
def _handle_bundle_update(stanza):
    log.info('Bundle Information received.')
    omemo_state = ProfOmemoState()
    bundle_info = xmpp.unpack_bundle_info(stanza)

    if not bundle_info:
        log.error('Could not unpack bundle info.')
        return

    sender = bundle_info.get('sender')
    device_id = bundle_info.get('device')

    try:
        omemo_state.build_session(sender, device_id, bundle_info)
        log.info('Session built with user: {0}:{1}'.format(sender, device_id))
        prof.completer_add('/omemo end', [sender])
    except Exception as e:
        msg_tmpl = 'Could not build session with {0}:{1}. {2}:{3}'
        msg = msg_tmpl.format(sender, device_id, type(e).__name__, str(e))
        log.error(msg)
        return
Beispiel #15
0
def create_own_bundle_stanza():
    announce_template = ('<iq from="{from_jid}" type="set" id="{req_id}">'
                         '<pubsub xmlns="http://jabber.org/protocol/pubsub">'
                         '<publish node="{bundles_ns}:{device_id}">'
                         '<item>'
                         '<bundle xmlns="{omemo_ns}">'
                         '</bundle>'
                         '</item>'
                         '</publish>'
                         '</pubsub>'
                         '</iq>')

    omemo_state = ProfOmemoState()
    own_bundle = omemo_state.bundle
    own_jid = omemo_state.own_jid
    bundle_msg = announce_template.format(from_jid=own_jid,
                                          req_id=str(uuid.uuid4()),
                                          device_id=omemo_state.own_device_id,
                                          bundles_ns=NS_BUNDLES,
                                          omemo_ns=NS_OMEMO)

    bundle_xml = ET.fromstring(bundle_msg)

    # to be appended to announce_template
    find_str = './/{%s}bundle' % NS_OMEMO
    bundle_node = bundle_xml.find(find_str)
    pre_key_signed_node = ET.SubElement(bundle_node, 'signedPreKeyPublic',
                                        attrib={'signedPreKeyId': str(
                                            own_bundle['signedPreKeyId'])})
    pre_key_signed_node.text = own_bundle.get('signedPreKeyPublic')

    signedPreKeySignature_node = ET.SubElement(bundle_node,
                                               'signedPreKeySignature')
    signedPreKeySignature_node.text = own_bundle.get('signedPreKeySignature')

    identityKey_node = ET.SubElement(bundle_node, 'identityKey')
    identityKey_node.text = own_bundle.get('identityKey')

    prekeys_node = ET.SubElement(bundle_node, 'prekeys')
    for key_id, key in own_bundle.get('prekeys', []):
        key_node = ET.SubElement(prekeys_node, 'preKeyPublic',
                                 attrib={'preKeyId': str(key_id)})
        key_node.text = key

    # reconvert xml to stanza
    try:
        bundle_stanza = ET.tostring(bundle_xml, encoding='utf-8', method='html')
    except:
        logger.exception('Could not convert Bunle XML to Stanza.')
        raise CouldNotCreateBundleStanza

    return bundle_stanza
Beispiel #16
0
def _handle_devicelist_update(stanza):
    omemo_state = ProfOmemoState()
    own_jid = omemo_state.own_jid
    msg_dict = xmpp.unpack_devicelist_info(stanza)
    sender_jid = msg_dict['from']
    log.info('Received devicelist update from {0}'.format(sender_jid))

    known_devices = omemo_state.device_list_for(sender_jid)
    new_devices = msg_dict['devices']

    added_devices = set(new_devices) - known_devices

    if added_devices:
        device_str = ', '.join([str(d) for d in added_devices])
        msg = '{0} added devices with IDs {1}'.format(sender_jid, device_str)
        show_chat_warning(sender_jid, msg)
        xmpp.update_devicelist(own_jid, sender_jid, new_devices)

    if not omemo_state.own_device_id_published():
        _announce_own_devicelist()

    if sender_jid != own_jid:
        add_recipient_to_completer(sender_jid)
def _handle_devicelist_update(stanza):
    omemo_state = ProfOmemoState()
    own_jid = omemo_state.own_jid
    msg_dict = xmpp.unpack_devicelist_info(stanza)
    sender_jid = msg_dict['from']
    log.info('Received devicelist update from {0}'.format(sender_jid))

    known_devices = omemo_state.device_list_for(sender_jid)
    new_devices = msg_dict['devices']

    added_devices = set(new_devices) - known_devices

    if added_devices:
        device_str = ', '.join([str(d) for d in added_devices])
        msg = '{0} added devices with IDs {1}'.format(sender_jid, device_str)
        show_chat_warning(sender_jid, msg)
        xmpp.update_devicelist(own_jid, sender_jid, new_devices)

    if not omemo_state.own_device_id_published():
        _announce_own_devicelist()

    if sender_jid != own_jid:
        add_recipient_to_completer(sender_jid)
Beispiel #18
0
def update_devicelist(from_jid, recipient, devices):
    omemo_state = ProfOmemoState()

    logger.debug('Update devices for account: {0}'.format(from_jid))
    logger.info('Adding Device ID\'s: {0} for {1}.'.format(devices, recipient))
    if devices:
        if from_jid == recipient:
            logger.info('Adding own devices')
            omemo_state.set_own_devices(devices)
        else:
            logger.info('Adding recipients devices')
            omemo_state.set_devices(recipient, devices)

        omemo_state.store.sessionStore.setActiveState(devices, from_jid)
        logger.info('Device List update done.')
Beispiel #19
0
def _parse_args(arg1=None, arg2=None, arg3=None):
    """ Parse arguments given in command window

    arg1: start || end
    arg2: muc || jid (optional)

    Starts or ends an encrypted chat session

    """
    account = ProfOmemoUser().account
    fulljid = ProfOmemoUser().fulljid

    if arg1 == 'on':
        _set_omemo_enabled_setting(True)

    elif arg1 == 'off':
        _set_omemo_enabled_setting(False)

    elif arg1 == 'start':
        # ensure we are in a chat window

        current_recipient = prof.get_current_recipient()

        if not current_recipient and arg2 != current_recipient:
            log.info('Opening Chat Window for {0}'.format(arg2))
            prof.send_line('/msg {0}'.format(arg2))

        recipient = arg2 or current_recipient
        if recipient:
            log.info('Start OMEMO session with: {0}'.format(recipient))
            _start_omemo_session(recipient)

    elif arg1 == 'end':
        # ensure we are in a chat window
        jid = arg2 or prof.get_current_muc() or prof.get_current_recipient()
        log.info('Ending OMEMO session with: {0}'.format(jid))
        if jid:
            _end_omemo_session(jid)

    elif arg1 == 'set':
        if arg2 == 'message_prefix':
            if arg3 is not None:
                _set_omemo_message_char(arg3)

    elif arg1 == 'account':
        prof.cons_show('Account: {0}'.format(account))

    elif arg1 == 'status':
        enabled = _get_omemo_enabled_setting()
        prof.cons_show('OMEMO Plugin Enabled: {0}'.format(enabled))

    elif arg1 == 'fulljid':
        prof.cons_show('Current JID: {0}'.format(fulljid))

    elif arg1 == 'show_devices' and arg2 is not None:
        account = arg2
        omemo_state = ProfOmemoState()
        prof.cons_show('Requesting Devices...')
        devices = omemo_state.device_list_for(account)
        prof.cons_show('Devices: {0}'.format(devices))
        prof.cons_show('{0}: {1}'.format(account, ', '.join(devices)))

    elif arg1 == 'reset_devicelist' and arg2 is not None:
        contact_jid = arg2
        if contact_jid != ProfOmemoUser.account:
            omemo_state = ProfOmemoState()
            omemo_state.set_devices(contact_jid, [])
            _query_device_list(contact_jid)

    elif arg1 == 'fingerprints':
        if arg2:
            contact_jid = query_jid = arg2
        else:
            # The local account is identified as '-1' in the OMEMO database
            contact_jid = ProfOmemoUser.account
            query_jid = '-1'
        omemo_state = ProfOmemoState()

        fingerprints = omemo_state.getFingerprints(query_jid)
        prof.cons_show('Fingerprints for account: {0}'.format(contact_jid))

        for record in fingerprints:
            _id, recipient_id, public_key, trust = record
            fpr = binascii.hexlify(public_key)
            fpr = human_hash(fpr[2:])

            prof.cons_show(' {0}'.format(fpr))

    else:
        prof.cons_show('Argument {0} not supported.'.format(arg1))
Beispiel #20
0
def prof_on_message_stanza_receive(stanza):
    stanza = ensure_unicode_stanza(stanza)

    log.info('Received Message: {0}'.format(stanza))
    if xmpp.is_devicelist_update(stanza):
        log.info('Device List update detected.')
        try:
            _handle_devicelist_update(stanza)
        except:
            log.exception('Failed to handle devicelist update.')

        return False

    if xmpp.is_encrypted_message(stanza):
        log.info('Received OMEMO encrypted message.')
        omemo_state = ProfOmemoState()

        try:
            msg_dict = xmpp.unpack_encrypted_stanza(stanza)
            sender = msg_dict['sender_jid']
            resource = msg_dict['sender_resource']
            sender_fulljid = sender + '/' + resource

            if sender_fulljid == ProfOmemoUser().fulljid:
                log.debug('Skipping own Message.')
                return False

            try:
                plain_msg = omemo_state.decrypt_msg(msg_dict)
            except Exception:
                log.exception('Could not decrypt Message.')
                return False

            if plain_msg is None:
                log.error('Could not decrypt Message')
                return True

            if plain_msg:
                # only mark the message if it was an OMEMO encrypted message
                try:
                    message_char = _get_omemo_message_char()
                    log.debug('Set incoming Message Character: {0}'.format(
                        message_char))
                    prof.chat_set_incoming_char(sender, message_char)
                    prof.incoming_message(sender, resource, plain_msg)
                finally:
                    prof.chat_unset_incoming_char(sender)

                # if this was the first OMEMO encrypted message received by
                # the sender (a.k.a. whenever profanity opens a new chat window
                # for a recipient), we automatically respond with OMEMO encrypted
                # messages. If encryption is turned off later by the user,
                # we respect that.
                if not ProfActiveOmemoChats.account_is_active(sender):
                    if not ProfActiveOmemoChats.account_is_deactivated(sender):
                        _start_omemo_session(sender)

            return False

        except Exception:
            # maybe not OMEMO encrypted, profanity will take care then
            log.exception('Could not handle encrypted message.')

    return True
def _parse_args(arg1=None, arg2=None, arg3=None):
    """ Parse arguments given in command window

    arg1: start || end
    arg2: muc || jid (optional)

    Starts or ends an encrypted chat session

    """
    account = ProfOmemoUser().account
    fulljid = ProfOmemoUser().fulljid

    if arg1 == 'on':
        _set_omemo_enabled_setting(True)

    elif arg1 == 'off':
        _set_omemo_enabled_setting(False)

    elif arg1 == 'start':
        # ensure we are in a chat window

        current_recipient = prof.get_current_recipient()

        if not current_recipient and arg2 != current_recipient:
            log.info('Opening Chat Window for {0}'.format(arg2))
            prof.send_line('/msg {0}'.format(arg2))

        recipient = arg2 or current_recipient
        if recipient:
            log.info('Start OMEMO session with: {0}'.format(recipient))
            _start_omemo_session(recipient)

    elif arg1 == 'end':
        # ensure we are in a chat window
        jid = arg2 or prof.get_current_muc() or prof.get_current_recipient()
        log.info('Ending OMEMO session with: {0}'.format(jid))
        if jid:
            _end_omemo_session(jid)

    elif arg1 == 'set':
        if arg2 == 'message_prefix':
            if arg3 is not None:
                _set_omemo_message_char(arg3)

    elif arg1 == 'account':
        prof.cons_show('Account: {0}'.format(account))

    elif arg1 == 'status':
        enabled = _get_omemo_enabled_setting()
        prof.cons_show('OMEMO Plugin Enabled: {0}'.format(enabled))

    elif arg1 == 'fulljid':
        prof.cons_show('Current JID: {0}'.format(fulljid))

    elif arg1 == 'show_devices' and arg2 is not None:
        account = arg2
        omemo_state = ProfOmemoState()
        prof.cons_show('Requesting Devices...')
        devices = omemo_state.device_list_for(account)
        prof.cons_show('Devices: {0}'.format(devices))
        prof.cons_show('{0}: {1}'.format(account, ', '.join(devices)))

    elif arg1 == 'reset_devicelist' and arg2 is not None:
        contact_jid = arg2
        if contact_jid != ProfOmemoUser.account:
            omemo_state = ProfOmemoState()
            omemo_state.set_devices(contact_jid, [])
            _query_device_list(contact_jid)

    elif arg1 == 'fingerprints':
        if arg2:
            contact_jid = query_jid = arg2
        else:
            # The local account is identified as '-1' in the OMEMO database
            contact_jid = ProfOmemoUser.account
            query_jid = '-1'
        omemo_state = ProfOmemoState()

        fingerprints = omemo_state.getFingerprints(query_jid)
        prof.cons_show('Fingerprints for account: {0}'.format(contact_jid))

        for record in fingerprints:
            _id, recipient_id, public_key, trust = record
            fpr = binascii.hexlify(public_key)
            fpr = human_hash(fpr[2:])

            prof.cons_show(' {0}'.format(fpr))

    else:
        prof.cons_show('Argument {0} not supported.'.format(arg1))
def prof_on_message_stanza_receive(stanza):
    stanza = ensure_unicode_stanza(stanza)

    log.info('Received Message: {0}'.format(stanza))
    if xmpp.is_devicelist_update(stanza):
        log.info('Device List update detected.')
        try:
            _handle_devicelist_update(stanza)
        except:
            log.exception('Failed to handle devicelist update.')

        return False

    if xmpp.is_encrypted_message(stanza):
        log.info('Received OMEMO encrypted message.')
        omemo_state = ProfOmemoState()

        try:
            msg_dict = xmpp.unpack_encrypted_stanza(stanza)
            sender = msg_dict['sender_jid']
            resource = msg_dict['sender_resource']
            sender_fulljid = sender + '/' + resource

            if sender_fulljid == ProfOmemoUser().fulljid:
                log.debug('Skipping own Message.')
                return False

            try:
                plain_msg = omemo_state.decrypt_msg(msg_dict)
            except Exception:
                log.exception('Could not decrypt Message.')
                return False

            if plain_msg is None:
                log.error('Could not decrypt Message')
                return True

            if plain_msg:
                # only mark the message if it was an OMEMO encrypted message
                try:
                    message_char = _get_omemo_message_char()
                    log.debug('Set incoming Message Character: {0}'.format(message_char))
                    prof.chat_set_incoming_char(sender, message_char)
                    prof.incoming_message(sender, resource, plain_msg)
                finally:
                    prof.chat_unset_incoming_char(sender)

                # if this was the first OMEMO encrypted message received by
                # the sender (a.k.a. whenever profanity opens a new chat window
                # for a recipient), we automatically respond with OMEMO encrypted
                # messages. If encryption is turned off later by the user,
                # we respect that.
                if not ProfActiveOmemoChats.account_is_active(sender):
                    if not ProfActiveOmemoChats.account_is_deactivated(sender):
                        _start_omemo_session(sender)

            return False

        except Exception:
            # maybe not OMEMO encrypted, profanity will take care then
            log.exception('Could not handle encrypted message.')

    return True