diff --git a/omemo/config_dialog.ui b/omemo/config_dialog.ui index df0b849..604121d 100644 --- a/omemo/config_dialog.ui +++ b/omemo/config_dialog.ui @@ -1,417 +1,641 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - - - True - False - 12 - 10 - - - True - False - 5 - - - True - False - <b>Account:</b> - True - - - False - True - 0 - - - - - True - False - account_store - - - - - 0 - - - - - False - True - 1 - - - - - False - True - 0 - - - - - True - False - - - 110 - True - False - 0 - Own Fingerprint: - - - - - - False - True - 0 - - - - - True - False - <tt>-------- -------- -------- -------- -------- </tt> - True - True - - - False - True - 1 - - - - - False - True - 1 - - - - - True - False - - - 110 - True - False - 0 - Own Device ID: - - - - - - False - False - 0 - - - - - True - False - 0 - 0 - - - False - True - 1 - - - - - False - False - 2 - - - - - - - True - False - Own Fingerprints - - - False - - - - - True - False - 12 - 10 - - - 200 - True - True - automatic - automatic - - - 300 - True - True - fingerprint_store - 0 - 3 - - - - True - Name - - - - 1 - - - - - - - True - Trust - - - - 2 - - - - - - - True - Fingerprint - - - - 3 - - - - - - - - - True - True - 0 - - - - - True - False - 5 - - - Trust/Revoke Fingerprint - 200 - True - True - True - - - - False - False - 0 - - - - - False - False - 1 - - - - - 1 - - - - - True - False - Known Fingerprints - - - 1 - False - - - - - True - False - 12 - 10 - - - 25 - True - False - 0 - Published Devices - - - - - - - False - True - 7 - 0 - - - - - True - True - never - automatic - - - True - False - deviceid_store - 0 - - - Device ID - - - - 0 - - - - - - - - - True - True - 1 - - - - - True - False - 5 - - - Clear Devices - 160 - True - True - True - - - - False - False - 0 - - - - - gtk-refresh - 160 - True - True - True - True - - - - False - False - 1 - - - - - False - False - 2 - - - - - 2 - - - - - True - False - Clear Devices - - - 2 - False - - - - - True - False - - - True - False - Copy to clipboard - True - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + + + True + False + 12 + 10 + + + True + False + 5 + + + True + False + <b>Account:</b> + True + + + False + True + 0 + + + + + True + False + account_store + + + + + 0 + + + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + + + 110 + True + False + Own Fingerprint: + 0 + + + + + + False + True + 0 + + + + + True + False + True + True + + + False + True + 1 + + + + + False + True + 1 + + + + + True + False + + + 110 + True + False + Own Device ID: + 0 + + + + + + False + False + 0 + + + + + True + False + 0 + + + False + True + 1 + + + + + False + False + 2 + + + + + True + False + 0 + gtk-missing-image + 0 + + + True + True + 3 + + + + + False + True + For Verification QRCode please install python-qrcode + 0 + + + + + + + True + True + 4 + + + + + + + True + False + Own Fingerprints + + + False + + + + + True + False + 12 + 10 + + + 200 + True + True + + + 300 + True + True + fingerprint_store + 0 + 3 + + + + + + + True + Name + True + True + 1 + + + + 1 + + + + + + + True + Trust + + + + 2 + + + + + + + True + Fingerprint + + + + 3 + + + + + + + + + True + True + 0 + + + + + True + False + 5 + + + Trust/Revoke Fingerprint + 200 + True + True + True + + + + False + False + 0 + + + + + Delete Fingerprint + 200 + True + True + True + + + + False + False + 1 + + + + + False + False + 1 + + + + + 1 + + + + + True + False + Known Fingerprints + + + 1 + False + + + + + True + False + 12 + 10 + + + 25 + True + False + Published Devices + 0 + + + + + + + False + True + 7 + 0 + + + + + True + True + never + + + True + False + deviceid_store + 0 + + + + + + Device ID + + + + 0 + + + + + + + + + True + True + 1 + + + + + True + False + 5 + + + Clear Devices + 160 + True + True + True + + + + False + False + 0 + + + + + gtk-refresh + 160 + True + True + True + True + + + + False + False + 1 + + + + + False + False + 2 + + + + + 2 + + + + + True + False + Clear Devices + + + 2 + False + + + + + True + False + + + 30 + True + False + You have to restart Gajim for changes to take effect ! + + + + + + + False + True + 0 + + + + + True + False + 12 + 5 + + + True + False + + + True + True + + + True + True + account_store + + + + + + Active Accounts + 0.5 + + + + 0 + + + + + + + + + True + True + 0 + + + + + Disable Account + True + True + True + + + + False + False + 1 + + + + + True + True + 0 + + + + + True + False + + + True + True + + + True + True + disabled_account_store + + + + + + Disabled Accounts + 0.5 + + + + 0 + + + + + + + + + True + True + 0 + + + + + Activate Account + True + True + True + + + + False + False + 1 + + + + + True + True + 1 + + + + + True + True + 1 + + + + + 3 + + + + + True + False + Disable Accounts + + + 3 + False + + + + + True + False + + + True + False + Copy to clipboard + True + + + + + diff --git a/omemo/fpr_dialog.ui b/omemo/fpr_dialog.ui index 76fbcc5..969aebe 100644 --- a/omemo/fpr_dialog.ui +++ b/omemo/fpr_dialog.ui @@ -1,298 +1,299 @@ - - - - - - - - - - - - - - - - - - - - - - - - True - True - - - - True - False - 12 - 10 - - - 200 - True - True - automatic - automatic - - - True - True - fingerprint_store - 0 - 3 - - - - True - Name - - - - 1 - - - - - - - True - Trust - - - - 2 - - - - - - - True - Fingerprint - - - - 3 - - - - - - - - - True - True - 0 - - - - - True - False - 5 - - - Trust/Revoke Fingerprint - 200 - True - True - True - - - - False - False - 0 - - - - - False - True - 1 - - - - - - - True - False - Contact - - - False - - - - - True - False - 12 - 10 - - - True - False - 10 - - - True - False - Own Fingerprint: - - - - - - False - True - 0 - - - - - True - False - <tt>-------- -------- -------- -------- -------- </tt> - True - True - - - False - False - 1 - - - - - False - False - 0 - - - - - 100 - True - True - automatic - automatic - - - True - True - True - fingerprint_store - False - 0 - 3 - - - - True - Name - - - - 1 - - - - - - - True - Trust - - - - 2 - - - - - - - True - Fingerprint - - - - 3 - - - - - - - - - True - True - 1 - - - - - True - False - 5 - - - Trust/Revoke Fingerprint - 200 - True - True - True - - - - False - False - 0 - - - - - False - True - 2 - - - - - 1 - - - - - True - False - Own Devices - - - 1 - False - - - - - True - False - - - True - False - Copy to clipboard - True - - - - - + + + + + + + + + + + + + + + + + + + + True + True + + + + True + False + 12 + 10 + + + 200 + True + True + + + True + True + True + fingerprint_store + False + 0 + 3 + + + + + + + True + Name + True + True + 1 + + + + 1 + + + + + + + True + Trust + + + + 2 + + + + + + + True + Fingerprint + + + + 3 + + + + + + + + + True + True + 0 + + + + + True + False + 5 + + + Trust/Revoke Fingerprint + 200 + True + True + True + + + + False + False + 0 + + + + + False + True + 1 + + + + + + + True + False + Contact + + + False + + + + + True + False + 12 + 10 + + + True + False + 10 + + + True + False + Own Fingerprint: + + + + + + False + True + 0 + + + + + True + False + <tt>-------- -------- -------- -------- -------- </tt> + True + True + + + False + False + 1 + + + + + False + False + 0 + + + + + 100 + True + True + + + True + True + True + fingerprint_store + False + 0 + 3 + + + + + + + True + Name + + + + 1 + + + + + + + True + Trust + + + + 2 + + + + + + + True + Fingerprint + + + + 3 + + + + + + + + + True + True + 1 + + + + + True + False + 5 + + + Trust/Revoke Fingerprint + 200 + True + True + True + + + + False + False + 0 + + + + + False + True + 2 + + + + + 1 + + + + + True + False + Own Devices + + + 1 + False + + + + + True + False + + + True + False + + + + + diff --git a/omemo/ui.py b/omemo/ui.py index e81813a..464913c 100644 --- a/omemo/ui.py +++ b/omemo/ui.py @@ -20,18 +20,30 @@ import binascii import logging +import os +import message_control -from gi.repository import GObject -from gi.repository import Gtk +from gi.repository import GObject, Gtk, GdkPixbuf # pylint: disable=import-error import gtkgui_helpers from common import gajim +from dialogs import YesNoDialog from plugins.gui import GajimPluginConfigDialog -# pylint: enable=import-error +from axolotl.state.sessionrecord import SessionRecord +from common import configpaths log = logging.getLogger('gajim.plugin_system.omemo') +PILLOW = False +try: + import qrcode + PILLOW = True +except Exception as e: + log.exception('Error:') + log.error('python-qrcode or dependencies of it, are not available') + +# pylint: enable=import-error UNDECIDED = 2 TRUSTED = 1 UNTRUSTED = 0 @@ -106,6 +118,12 @@ class Ui(object): self.account = self.contact.account.name self.windowinstances = {} + self.groupchat = False + if chat_control.type_id == message_control.TYPE_GC: + self.groupchat = True + self.omemo_capable = False + self.room = self.chat_control.room_jid + self.display_omemo_state() self.refresh_auth_lock_icon() @@ -133,6 +151,9 @@ class Ui(object): item.set_image(Gtk.Image.new_from_file(icon_path)) item.set_submenu(submenu) + if self.groupchat: + item.set_sensitive(self.omemo_capable) + # at index 8 is the separator after the esession encryption entry menu.insert(item, 8) return menu @@ -159,7 +180,38 @@ class Ui(object): log.debug(self.account + ' => Sending Message to ' + self.contact.jid) - self.chat_control.send_message = omemo_send_message + def omemo_send_gc_message(message, xhtml=None, process_commands=True): + self.new_fingerprints_available() + if self.encryption_active(): + missing = True + own_jid = gajim.get_jid_from_account(self.account) + for nick in self.plugin.groupchat[self.room]: + real_jid = self.plugin.groupchat[self.room][nick] + if real_jid == own_jid: + continue + if not self.plugin.are_keys_missing(self.account, + real_jid): + missing = False + if missing: + log.debug(self.account + + ' => No Trusted Fingerprints for ' + + self.room) + self.no_trusted_fingerprints_warning() + else: + self.chat_control.orig_send_message(message, xhtml, + process_commands) + log.debug(self.account + ' => Sending Message to ' + + self.room) + else: + self.chat_control.orig_send_message(message, xhtml, + process_commands) + log.debug(self.account + ' => Sending Message to ' + + self.room) + + if self.groupchat: + self.chat_control.send_message = omemo_send_gc_message + else: + self.chat_control.send_message = omemo_send_message def set_omemo_state(self, enabled): """ @@ -182,6 +234,12 @@ class Ui(object): self.omemobutton.set_omemo_state(enabled) self.display_omemo_state() + def sensitive(self, value): + self.omemobutton.set_sensitive(value) + self.omemo_capable = value + if value: + self.chat_control.prepare_context_menu + def encryption_active(self): return self.state.encryption.is_active(self.contact.jid) @@ -190,16 +248,31 @@ class Ui(object): self.set_omemo_state(True) def new_fingerprints_available(self): - fingerprints = self.state.store.getNewFingerprints(self.contact.jid) - if fingerprints: - self.show_fingerprint_window(fingerprints) + jid = self.contact.jid + if self.groupchat and self.room in self.plugin.groupchat: + for nick in self.plugin.groupchat[self.room]: + real_jid = self.plugin.groupchat[self.room][nick] + fingerprints = self.state.store. \ + getNewFingerprints(real_jid) + if fingerprints: + self.show_fingerprint_window(fingerprints) + elif not self.groupchat: + fingerprints = self.state.store.getNewFingerprints(jid) + if fingerprints: + self.show_fingerprint_window(fingerprints) def show_fingerprint_window(self, fingerprints=None): if 'dialog' not in self.windowinstances: - self.windowinstances['dialog'] = \ - FingerprintWindow(self.plugin, self.contact, - self.chat_control.parent_win.window, - self.windowinstances) + if self.groupchat: + self.windowinstances['dialog'] = \ + FingerprintWindow(self.plugin, self.contact, + self.chat_control.parent_win.window, + self.windowinstances, groupchat=True) + else: + self.windowinstances['dialog'] = \ + FingerprintWindow(self.plugin, self.contact, + self.chat_control.parent_win.window, + self.windowinstances) self.windowinstances['dialog'].show_all() if fingerprints: log.debug(self.account + @@ -225,10 +298,12 @@ class Ui(object): def no_trusted_fingerprints_warning(self): msg = "To send an encrypted message, you have to " \ - "first trust the fingerprint of your contact!" + "first trust the fingerprint of your contact!" self.chat_control.print_conversation_line(msg, 'status', '', None) def refresh_auth_lock_icon(self): + if self.groupchat: + return if self.encryption_active(): if self.state.getUndecidedFingerprints(self.contact.jid): self.chat_control._show_lock_image(True, 'OMEMO', True, True, @@ -242,6 +317,8 @@ class Ui(object): def removeUi(self): self.actions_hbox.remove(self.omemobutton) + self.chat_control._show_lock_image(False, 'OMEMO', False, True, + False) self.chat_control.prepare_context_menu = \ self.chat_control.omemo_orig_prepare_context_menu self.chat_control.send_message = self.chat_control.orig_send_message @@ -256,40 +333,144 @@ class OMEMOConfigDialog(GajimPluginConfigDialog): self.B.set_translation_domain('gajim_plugins') self.B.add_from_file(self.GTK_BUILDER_FILE_PATH) - self.fpr_model = Gtk.ListStore(GObject.TYPE_INT, - GObject.TYPE_STRING, - GObject.TYPE_STRING, - GObject.TYPE_STRING) + try: + self.disabled_accounts = self.plugin.config['DISABLED_ACCOUNTS'] + except KeyError: + self.plugin.config['DISABLED_ACCOUNTS'] = [] + self.disabled_accounts = self.plugin.config['DISABLED_ACCOUNTS'] - self.device_model = Gtk.ListStore(GObject.TYPE_INT) + log.debug('Disabled Accounts:') + log.debug(self.disabled_accounts) - self.account_store = self.B.get_object('account_store') - - for account in sorted(gajim.contacts.get_accounts()): - self.account_store.append(row=(account,)) + self.fpr_model = self.B.get_object('fingerprint_store') + self.device_model = self.B.get_object('deviceid_store') self.fpr_view = self.B.get_object('fingerprint_view') - self.fpr_view.set_model(self.fpr_model) - self.fpr_view.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) - self.device_view = self.B.get_object('deviceid_view') - self.device_view.set_model(self.device_model) + self.disabled_acc_store = self.B.get_object('disabled_account_store') + self.account_store = self.B.get_object('account_store') - if len(self.account_store) > 0: - self.B.get_object('account_combobox').set_active(0) + self.active_acc_view = self.B.get_object('active_accounts_view') + self.disabled_acc_view = self.B.get_object('disabled_accounts_view') vbox = self.get_content_area() vbox.pack_start(self.B.get_object('notebook1'), True, True, 0) self.B.connect_signals(self) + self.plugin_active = False + def on_run(self): - self.update_context_list() - self.account_combobox_changed_cb(self.B.get_object('account_combobox')) + for plugin in gajim.plugin_manager.active_plugins: + log.debug(type(plugin)) + if type(plugin).__name__ == 'OmemoPlugin': + self.plugin_active = True + break + self.update_account_store() + self.update_account_combobox() + self.update_disabled_account_view() + + def is_in_accountstore(self, account): + for row in self.account_store: + if row[0] == account: + return True + return False + + def update_account_store(self): + for account in sorted(gajim.contacts.get_accounts()): + if account not in self.disabled_accounts and \ + not self.is_in_accountstore(account): + self.account_store.append(row=(account,)) + + def update_account_combobox(self): + if self.plugin_active is False: + return + if len(self.account_store) > 0: + self.B.get_object('account_combobox').set_active(0) + else: + self.account_combobox_changed_cb( + self.B.get_object('account_combobox')) def account_combobox_changed_cb(self, box, *args): self.update_context_list() + def get_qrcode(self, jid, sid, fingerprint): + file_name = 'omemo_{}.png'.format(jid) + path = os.path.join( + configpaths.gajimpaths['MY_DATA'], file_name) + + ver_string = 'xmpp:{}?omemo-sid-{}={}'.format(jid, sid, fingerprint) + log.debug('Verification String: ' + ver_string) + + if os.path.exists(path): + return path + + qr = qrcode.QRCode(version=None, error_correction=2, box_size=4, border=1) + qr.add_data(ver_string) + qr.make(fit=True) + img = qr.make_image() + img.save(path) + return path + + def update_disabled_account_view(self): + self.disabled_acc_store.clear() + for account in self.disabled_accounts: + self.disabled_acc_store.append(row=(account,)) + + def activate_accounts_btn_clicked(self, button, *args): + mod, paths = self.disabled_acc_view.get_selection().get_selected_rows() + for path in paths: + it = mod.get_iter(path) + account = mod.get(it, 0) + if account[0] in self.disabled_accounts and \ + not self.is_in_accountstore(account[0]): + self.account_store.append(row=(account[0],)) + self.disabled_accounts.remove(account[0]) + self.update_disabled_account_view() + self.plugin.config['DISABLED_ACCOUNTS'] = self.disabled_accounts + self.update_account_combobox() + + def disable_accounts_btn_clicked(self, button, *args): + mod, paths = self.active_acc_view.get_selection().get_selected_rows() + for path in paths: + it = mod.get_iter(path) + account = mod.get(it, 0) + if account[0] not in self.disabled_accounts and \ + self.is_in_accountstore(account[0]): + self.disabled_accounts.append(account[0]) + self.account_store.remove(it) + self.update_disabled_account_view() + self.plugin.config['DISABLED_ACCOUNTS'] = self.disabled_accounts + self.update_account_combobox() + + def delfpr_button_clicked(self, button, *args): + active = self.B.get_object('account_combobox').get_active() + account = self.account_store[active][0] + + state = self.plugin.get_omemo_state(account) + + mod, paths = self.fpr_view.get_selection().get_selected_rows() + + def on_yes(checked): + record = state.store.loadSession(jid, deviceid) + identity_key = record.getSessionState().getRemoteIdentityKey() + + state.store.deleteSession(jid, deviceid) + state.store.deleteIdentity(jid, identity_key) + self.update_context_list() + + for path in paths: + it = mod.get_iter(path) + jid, fpr, deviceid = mod.get(it, 1, 3, 4) + fpr = fpr[31:-12] + + YesNoDialog( + 'Delete Fingerprint?', + 'Do you want to delete the ' + 'fingerprint of {} on your account {}?' + '\n\n{}'.format(jid, account, fpr), + on_response_yes=on_yes, transient_for=self) + def trust_button_clicked_cb(self, button, *args): active = self.B.get_object('account_combobox').get_active() account = self.account_store[active][0] @@ -298,43 +479,42 @@ class OMEMOConfigDialog(GajimPluginConfigDialog): mod, paths = self.fpr_view.get_selection().get_selected_rows() + def on_yes(checked, identity_key): + state.store.setTrust(identity_key, TRUSTED) + try: + if self.plugin.ui_list[account]: + self.plugin.ui_list[account][jid]. \ + refresh_auth_lock_icon() + except: + log.debug('UI not available') + self.update_context_list() + + def on_no(identity_key): + state.store.setTrust(identity_key, UNTRUSTED) + try: + if jid in self.plugin.ui_list[account]: + self.plugin.ui_list[account][jid]. \ + refresh_auth_lock_icon() + except: + log.debug('UI not available') + self.update_context_list() + for path in paths: it = mod.get_iter(path) - _id, user, fpr = mod.get(it, 0, 1, 3) + jid, fpr, deviceid = mod.get(it, 1, 3, 4) fpr = fpr[31:-12] - dlg = Gtk.Dialog('Trust / Revoke Fingerprint', self, - Gtk.DialogFlags.MODAL | - Gtk.DialogFlags.DESTROY_WITH_PARENT, - (Gtk.STOCK_YES, Gtk.ResponseType.YES, - Gtk.STOCK_NO, Gtk.ResponseType.NO)) - l = Gtk.Label() - l.set_markup('Do you want to trust the ' - 'fingerprint of %s on your account %s?' - '\n\n%s' % (user, account, fpr)) - l.set_line_wrap(True) - l.set_padding(12, 12) - vbox = dlg.get_content_area() - vbox.add(l) - dlg.show_all() - response = dlg.run() - if response == Gtk.ResponseType.YES: - state.store.identityKeyStore.setTrust(_id, TRUSTED) - try: - if self.plugin.ui_list[account]: - self.plugin.ui_list[account][user].refresh_auth_lock_icon() - except: - dlg.destroy() - else: - if response == Gtk.ResponseType.NO: - state.store.identityKeyStore.setTrust(_id, UNTRUSTED) - try: - if user in self.plugin.ui_list[account]: - self.plugin.ui_list[account][user].refresh_auth_lock_icon() - except: - dlg.destroy() + record = state.store.loadSession(jid, deviceid) + identity_key = record.getSessionState().getRemoteIdentityKey() - self.update_context_list() + YesNoDialog( + 'Trust / Revoke Fingerprint?', + 'Do you want to trust the fingerprint of {} ' + 'on your account {}?\n\n' + '{}'.format(jid, account, fpr), + on_response_yes=(on_yes, identity_key), + on_response_no=(on_no, identity_key), + transient_for=self) def cleardevice_button_clicked_cb(self, button, *args): active = self.B.get_object('account_combobox').get_active() @@ -380,67 +560,94 @@ class OMEMOConfigDialog(GajimPluginConfigDialog): def update_context_list(self): self.fpr_model.clear() self.device_model.clear() + self.qrcode = self.B.get_object('qrcode') + self.qrinfo = self.B.get_object('qrinfo') + if len(self.account_store) == 0: + self.B.get_object('ID').set_markup('') + self.B.get_object('fingerprint_label').set_markup('') + self.B.get_object('trust_button').set_sensitive(False) + self.B.get_object('delfprbutton').set_sensitive(False) + self.B.get_object('refresh').set_sensitive(False) + self.B.get_object('cleardevice_button').set_sensitive(False) + self.B.get_object('qrcode').clear() + return active = self.B.get_object('account_combobox').get_active() account = self.account_store[active][0] - state = self.plugin.get_omemo_state(account) + # Set buttons active + self.B.get_object('trust_button').set_sensitive(True) + self.B.get_object('delfprbutton').set_sensitive(True) + self.B.get_object('refresh').set_sensitive(True) + if account == 'Local': + self.B.get_object('cleardevice_button').set_sensitive(False) + else: + self.B.get_object('cleardevice_button').set_sensitive(True) + + # Set FPR Label and DeviceID + state = self.plugin.get_omemo_state(account) deviceid = state.own_device_id self.B.get_object('ID').set_markup('%s' % deviceid) ownfpr = binascii.hexlify(state.store.getIdentityKeyPair() .getPublicKey().serialize()).decode('utf-8') - ownfpr = self.human_hash(ownfpr[2:]) + human_ownfpr = human_hash(ownfpr[2:]) self.B.get_object('fingerprint_label').set_markup('%s' - % ownfpr) + % human_ownfpr) - fprDB = state.store.identityKeyStore.getAllFingerprints() - activeSessions = state.store.sessionStore. \ - getAllActiveSessionsKeys() - for item in fprDB: - _id, jid, fpr, tr = item - active = fpr in activeSessions - fpr = binascii.hexlify(fpr).decode('utf-8') - fpr = self.human_hash(fpr[2:]) - jid = jid.decode('utf-8') - if tr == UNTRUSTED: - if active: - self.fpr_model.append((_id, jid, 'False', - '%s' % fpr)) - else: - self.fpr_model.append((_id, jid, 'False', - '%s' % fpr)) - elif tr == TRUSTED: - if active: - self.fpr_model.append((_id, jid, 'True', - '%s' % fpr)) - else: - self.fpr_model.append((_id, jid, 'True', - '%s' % fpr)) - else: - if active: - self.fpr_model.append((_id, jid, 'Undecided', - '%s' % fpr)) - else: - self.fpr_model.append((_id, jid, 'Undecided', - '%s' % fpr)) + # Set Fingerprint List + trust_str = {0: 'False', 1: 'True', 2: 'Undecided'} + session_db = state.store.getAllSessions() + for item in session_db: + color = {0: '#FF0040', # red + 1: '#2EFE2E', # green + 2: '#FF8000'} # orange + + _id, jid, deviceid, record, active = item + + active = bool(active) + + identity_key = SessionRecord(serialized=record). \ + getSessionState().getRemoteIdentityKey() + fpr = binascii.hexlify( + identity_key.getPublicKey().serialize()).decode('utf-8') + fpr = human_hash(fpr[2:]) + + trust = state.store.isTrustedIdentity(jid, identity_key) + + if not active: + color[trust] = '#585858' # grey + + self.fpr_model.append( + (_id, jid, trust_str[trust], + '{}'. + format(color[trust], fpr), + deviceid)) + + # Set Device ID List for item in state.own_devices: self.device_model.append([item]) - def human_hash(self, fpr): - fpr = fpr.upper() - fplen = len(fpr) - wordsize = fplen // 8 - buf = '' - for w in range(0, fplen, wordsize): - buf += '{0} '.format(fpr[w:w + wordsize]) - return buf.rstrip() + # Set QR Verification Code + if PILLOW: + path = self.get_qrcode( + gajim.get_jid_from_account(account), deviceid, ownfpr[2:]) + self.qrcode.set_from_pixbuf(GdkPixbuf.Pixbuf.new_from_file(path)) + self.qrinfo.hide() + else: + self.qrinfo.show() class FingerprintWindow(Gtk.Dialog): - def __init__(self, plugin, contact, parent, windowinstances): + def __init__(self, plugin, contact, parent, windowinstances, + groupchat=False): + self.groupchat = groupchat self.contact = contact self.windowinstances = windowinstances + self.account = self.contact.account.name + self.plugin = plugin + self.omemostate = self.plugin.get_omemo_state(self.account) + self.own_jid = gajim.get_jid_from_account(self.account) Gtk.Dialog.__init__(self, title=('Fingerprints for %s') % contact.jid, parent=parent, @@ -448,45 +655,34 @@ class FingerprintWindow(Gtk.Dialog): close_button = self.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE) close_button.connect('clicked', self.on_close_button_clicked) self.connect('delete-event', self.on_window_delete) - self.plugin = plugin + self.GTK_BUILDER_FILE_PATH = \ self.plugin.local_file_path('fpr_dialog.ui') - self.B = Gtk.Builder() - self.B.set_translation_domain('gajim_plugins') - self.B.add_from_file(self.GTK_BUILDER_FILE_PATH) + self.xml = Gtk.Builder() + self.xml.add_from_file(self.GTK_BUILDER_FILE_PATH) + self.xml.set_translation_domain('gajim_plugins') - self.fpr_model = Gtk.ListStore(GObject.TYPE_INT, - GObject.TYPE_STRING, - GObject.TYPE_STRING, - GObject.TYPE_STRING) + self.fpr_model = self.xml.get_object('fingerprint_store') - self.fpr_view = self.B.get_object('fingerprint_view') - self.fpr_view.set_model(self.fpr_model) - self.fpr_view.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) - - self.fpr_view_own = self.B.get_object('fingerprint_view_own') - self.fpr_view_own.set_model(self.fpr_model) - self.fpr_view_own.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) - - self.notebook = self.B.get_object('notebook1') + self.fpr_view = self.xml.get_object('fingerprint_view') + self.fpr_view_own = self.xml.get_object('fingerprint_view_own') + self.notebook = self.xml.get_object('notebook1') vbox = self.get_content_area() vbox.pack_start(self.notebook, True, True, 0) - self.B.connect_signals(self) - - self.account = self.contact.account.name - self.omemostate = self.plugin.get_omemo_state(self.account) + self.xml.connect_signals(self) + # Set own Fingerprint Label ownfpr = binascii.hexlify(self.omemostate.store.getIdentityKeyPair() .getPublicKey().serialize()).decode('utf-8') - ownfpr = self.human_hash(ownfpr[2:]) - - self.B.get_object('fingerprint_label_own').set_markup('%s' - % ownfpr) - + ownfpr = human_hash(ownfpr[2:]) + self.xml.get_object('fingerprint_label_own').set_markup('%s' + % ownfpr) self.update_context_list() + self.show_all() + def on_close_button_clicked(self, widget): del self.windowinstances['dialog'] self.hide() @@ -496,43 +692,43 @@ class FingerprintWindow(Gtk.Dialog): self.hide() def trust_button_clicked_cb(self, button, *args): + state = self.omemostate + if self.notebook.get_current_page() == 1: mod, paths = self.fpr_view_own.get_selection().get_selected_rows() else: mod, paths = self.fpr_view.get_selection().get_selected_rows() - for path in paths: - it = mod.get_iter(path) - _id, user, fpr = mod.get(it, 0, 1, 3) - fpr = fpr[31:-12] - dlg = Gtk.Dialog('Trust / Revoke Fingerprint', self, - Gtk.DialogFlags.MODAL | - Gtk.DialogFlags.DESTROY_WITH_PARENT, - (Gtk.STOCK_YES, Gtk.ResponseType.YES, - Gtk.STOCK_NO, Gtk.ResponseType.NO)) - l = Gtk.Label() - l.set_markup('Do you want to trust the ' - 'fingerprint of %s on your account %s?' - '\n\n%s' % (user, self.account, fpr)) - l.set_line_wrap(True) - l.set_padding(12, 12) - vbox = dlg.get_content_area() - vbox.add(l) - dlg.show_all() - response = dlg.run() - if response == Gtk.ResponseType.YES: - self.omemostate.store.identityKeyStore.setTrust(_id, TRUSTED) + def on_yes(checked, identity_key): + state.store.setTrust(identity_key, TRUSTED) + if not self.groupchat: self.plugin.ui_list[self.account][self.contact.jid]. \ refresh_auth_lock_icon() - dlg.destroy() - else: - if response == Gtk.ResponseType.NO: - self.omemostate.store.identityKeyStore.setTrust(_id, UNTRUSTED) - self.plugin.ui_list[self.account][self.contact.jid]. \ - refresh_auth_lock_icon() - dlg.destroy() + self.update_context_list() - self.update_context_list() + def on_no(identity_key): + state.store.setTrust(identity_key, UNTRUSTED) + if not self.groupchat: + self.plugin.ui_list[self.account][self.contact.jid]. \ + refresh_auth_lock_icon() + self.update_context_list() + + for path in paths: + it = mod.get_iter(path) + jid, fpr, deviceid = mod.get(it, 1, 3, 4) + fpr = fpr[31:-12] + + record = state.store.loadSession(jid, deviceid) + identity_key = record.getSessionState().getRemoteIdentityKey() + + YesNoDialog( + 'Trust / Revoke Fingerprint?', + 'Do you want to trust the fingerprint of {} ' + 'on your account {}?\n\n' + '{}'.format(jid, self.account, fpr), + on_response_yes=(on_yes, identity_key), + on_response_no=(on_no, identity_key), + transient_for=self) def fpr_button_pressed_cb(self, tw, event): if event.button == 3: @@ -547,7 +743,7 @@ class FingerprintWindow(Gtk.Dialog): # selection, otherwise we only select the new item keep_selection = tw.get_selection().path_is_selected(pthinfo[0]) - pop = self.B.get_object('fprclipboard_menu') + pop = self.xml.get_object('fprclipboard_menu') pop.popup(None, None, None, event.button, event.time) # keep_selection=True -> no further processing of click event @@ -571,49 +767,56 @@ class FingerprintWindow(Gtk.Dialog): def update_context_list(self, *args): self.fpr_model.clear() + state = self.omemostate if self.notebook.get_current_page() == 1: - jid = gajim.get_jid_from_account(self.account) + contact_jid = self.own_jid else: - jid = self.contact.jid + contact_jid = self.contact.jid - fprDB = self.omemostate.store.identityKeyStore.getFingerprints(jid) - activeSessions = self.omemostate.store.sessionStore. \ - getActiveSessionsKeys(jid) + trust_str = {0: 'False', 1: 'True', 2: 'Undecided'} + if self.groupchat and self.notebook.get_current_page() == 0: + contact_jids = [] + for nick in self.plugin.groupchat[contact_jid]: + real_jid = self.plugin.groupchat[contact_jid][nick] + if real_jid == self.own_jid: + continue + contact_jids.append(real_jid) + session_db = state.store.getSessionsFromJids(contact_jids) + else: + session_db = state.store.getSessionsFromJid(contact_jid) - for item in fprDB: - _id, jid, fpr, tr = item - active = fpr in activeSessions - fpr = binascii.hexlify(fpr).decode('utf-8') - fpr = self.human_hash(fpr[2:]) - jid = jid.decode('utf-8') - if tr == UNTRUSTED: - if active: - self.fpr_model.append((_id, jid, 'False', - '%s' % fpr)) - else: - self.fpr_model.append((_id, jid, 'False', - '%s' % fpr)) - elif tr == TRUSTED: - if active: - self.fpr_model.append((_id, jid, 'True', - '%s' % fpr)) - else: - self.fpr_model.append((_id, jid, 'True', - '%s' % fpr)) - else: - if active: - self.fpr_model.append((_id, jid, 'Undecided', - '%s' % fpr)) - else: - self.fpr_model.append((_id, jid, 'Undecided', - '%s' % fpr)) + for item in session_db: + color = {0: '#FF0040', # red + 1: '#2EFE2E', # green + 2: '#FF8000'} # orange - def human_hash(self, fpr): - fpr = fpr.upper() - fplen = len(fpr) - wordsize = fplen // 8 - buf = '' - for w in range(0, fplen, wordsize): - buf += '{0} '.format(fpr[w:w + wordsize]) - return buf.rstrip() + _id, jid, deviceid, record, active = item + + active = bool(active) + + identity_key = SessionRecord(serialized=record). \ + getSessionState().getRemoteIdentityKey() + fpr = binascii.hexlify(identity_key.getPublicKey().serialize()).decode('utf-8') + fpr = human_hash(fpr[2:]) + + trust = state.store.isTrustedIdentity(jid, identity_key) + + if not active: + color[trust] = '#585858' # grey + + self.fpr_model.append( + (_id, jid, trust_str[trust], + '{}'. + format(color[trust], fpr), + deviceid)) + + +def human_hash(fpr): + fpr = fpr.upper() + fplen = len(fpr) + wordsize = fplen // 8 + buf = '' + for w in range(0, fplen, wordsize): + buf += '{0} '.format(fpr[w:w + wordsize]) + return buf.rstrip()