def _start_omemo_session(jid): # should be started before the first message is sent. # we store the jid in ProfActiveOmemoChats as the user at least intends to # use OMEMO encryption. The respecting methods should then not ignore # sending OMEMO messages and fail if no session was created then. if not ProfActiveOmemoChats.account_is_registered(jid): ProfActiveOmemoChats.add(jid) else: ProfActiveOmemoChats.activate(jid) # ensure we have no running OTR session prof.encryption_reset(jid) # Visualize that OMEMO is now running prof.chat_set_titlebar_enctext(jid, 'OMEMO') message_char = _get_omemo_message_char() prof.chat_set_outgoing_char(jid, message_char) show_chat_info(jid, 'OMEMO Session started.') _show_no_trust_mgmt_header(jid) log.info('Query Devicelist for {0}'.format(jid)) _query_device_list(jid) prof.settings_string_list_add(SETTINGS_GROUP, 'omemo_sessions', jid)
def test_omemo_chat_remove_account(self): account = '*****@*****.**' assert len(ProfActiveOmemoChats._active) == 0 ProfActiveOmemoChats.add(account) assert len(ProfActiveOmemoChats._active) == 1 ProfActiveOmemoChats.remove(account) assert len(ProfActiveOmemoChats._active) == 0
def test_omemo_acitve_chat_is_singleton(self): account = '*****@*****.**' active_chats_instance = ProfActiveOmemoChats ProfActiveOmemoChats.add(account) new_active_chats_instance = ProfActiveOmemoChats assert active_chats_instance._active == new_active_chats_instance._active
def test_omemo_chat_adds_accounts_uniquely(self): account = '*****@*****.**' assert len(ProfActiveOmemoChats._active) == 0 ProfActiveOmemoChats.add(account) assert len(ProfActiveOmemoChats._active) == 1 ProfActiveOmemoChats.add(account) assert len(ProfActiveOmemoChats._active) == 1
def test_has_session_decorator_returns_default_on_error(self): @plugin.require_sessions_for_all_devices('not_valid_attrib') def func(x): return x recipient = '*****@*****.**' ProfActiveOmemoChats.add(recipient) stanza = '<message to="{}"></message>'.format(recipient) assert func(stanza) is None
def _end_omemo_session(jid): ProfActiveOmemoChats.deactivate(jid) # Release OMEMO from titlebar prof.chat_unset_titlebar_enctext(jid) prof.chat_unset_incoming_char(jid) prof.chat_unset_outgoing_char(jid) prof.settings_string_list_remove(SETTINGS_GROUP, 'omemo_sessions', jid) show_chat_info(jid, 'OMEMO Session ended.')
def test_stacked_decorators(self, settings_boolean_get): @plugin.omemo_enabled(else_return='enabled_first') @plugin.require_sessions_for_all_devices('to', else_return='session_second') def func(x): return x settings_boolean_get.return_value = True recipient = '*****@*****.**' ProfActiveOmemoChats.add(recipient) stanza = '<message to="{}"></message>'.format(recipient) assert func(stanza) == stanza
def test_has_session_decorator_returns_func_result_on_none_session(self, devices_mock): devices_mock.return_value = [] @plugin.require_sessions_for_all_devices('to') def func(x): return x recipient = '*****@*****.**' ProfActiveOmemoChats.add(recipient) stanza = '<message to="{}"></message>'.format(recipient) assert func(stanza) == stanza
def test_has_session_decorator_returns_func_result_on_none_session( self, devices_mock): devices_mock.return_value = [] @plugin.require_sessions_for_all_devices('to') def func(x): return x recipient = '*****@*****.**' ProfActiveOmemoChats.add(recipient) stanza = '<message to="{}"></message>'.format(recipient) assert func(stanza) == stanza
def prof_on_chat_win_focus(barejid): if not ProfActiveOmemoChats.account_is_registered(barejid): # get remembered user sessions u_sess = prof.settings_string_list_get(SETTINGS_GROUP, 'omemo_sessions') if u_sess and barejid in u_sess: _start_omemo_session(barejid)
def prof_on_message_stanza_send(stanza): stanza = ensure_unicode_stanza(stanza) contact_jid = xmpp.get_recipient(stanza) if not ProfActiveOmemoChats.account_is_active(contact_jid): log.debug('Chat not activated for {0}'.format(contact_jid)) return None try: if xmpp.is_xmpp_plaintext_message(stanza): encrypted_stanza = xmpp.encrypt_stanza(stanza) if xmpp.stanza_is_valid_xml(encrypted_stanza): return encrypted_stanza except Exception as e: log.exception('Could not encrypt message') show_chat_critical(contact_jid, 'Last message was sent unencrypted.') return None
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_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 teardown_method(self, test_method): ProfActiveOmemoChats.reset() ProfOmemoUser.reset()
def test_prof_active_chats_finds_active_chats(self): account = '*****@*****.**' account2 = '*****@*****.**' ProfActiveOmemoChats.add(account) ProfActiveOmemoChats.add(account2) assert ProfActiveOmemoChats.account_is_active(account) is True assert ProfActiveOmemoChats.account_is_active(account2) is True ProfActiveOmemoChats.remove(account) assert ProfActiveOmemoChats.account_is_active(account) is False ProfActiveOmemoChats.remove(account2) assert ProfActiveOmemoChats.account_is_active(account2) is False
def teardown_method(self, test_method): ProfOmemoUser.reset() ProfActiveOmemoChats.reset()
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