From dba577f39c489b9db29960f2bf5cc78ac4f29d7b Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 4 Oct 2026 14:42:16 +0200 Subject: Move OMEMO, OpenPGP and RTP plugins into main/libdino --- plugins/omemo/src/ui/bad_messages_populator.vala | 198 ------------- plugins/omemo/src/ui/call_encryption_entry.vala | 58 ---- plugins/omemo/src/ui/contact_details_provider.vala | 28 -- .../src/ui/device_notification_populator.vala | 90 ------ plugins/omemo/src/ui/encryption_list_entry.vala | 78 ----- .../omemo/src/ui/encryption_preferences_entry.vala | 319 --------------------- plugins/omemo/src/ui/manage_key_dialog.vala | 178 ------------ plugins/omemo/src/ui/own_notifications.vala | 35 --- plugins/omemo/src/ui/util.vala | 40 --- 9 files changed, 1024 deletions(-) delete mode 100644 plugins/omemo/src/ui/bad_messages_populator.vala delete mode 100644 plugins/omemo/src/ui/call_encryption_entry.vala delete mode 100644 plugins/omemo/src/ui/contact_details_provider.vala delete mode 100644 plugins/omemo/src/ui/device_notification_populator.vala delete mode 100644 plugins/omemo/src/ui/encryption_list_entry.vala delete mode 100644 plugins/omemo/src/ui/encryption_preferences_entry.vala delete mode 100644 plugins/omemo/src/ui/manage_key_dialog.vala delete mode 100644 plugins/omemo/src/ui/own_notifications.vala delete mode 100644 plugins/omemo/src/ui/util.vala (limited to 'plugins/omemo/src/ui') diff --git a/plugins/omemo/src/ui/bad_messages_populator.vala b/plugins/omemo/src/ui/bad_messages_populator.vala deleted file mode 100644 index c291a422..00000000 --- a/plugins/omemo/src/ui/bad_messages_populator.vala +++ /dev/null @@ -1,198 +0,0 @@ -using Gee; -using Gtk; - -using Dino.Entities; -using Dino.Qlite; -using Xmpp; - -namespace Dino.Plugins.Omemo { - -public enum BadnessType { - UNTRUSTED, - UNDECRYPTABLE -} - -public class BadMessagesPopulator : Plugins.ConversationItemPopulator, Plugins.ConversationAdditionPopulator, Object { - - public string id { get { return "bad_omemo_messages"; } } - - private StreamInteractor stream_interactor; - private Plugin plugin; - private Database db; - - private Conversation? current_conversation; - private Plugins.ConversationItemCollection? item_collection; - - private Gee.List bad_items = new ArrayList(); - - public BadMessagesPopulator(StreamInteractor stream_interactor, Plugin plugin) { - this.stream_interactor = stream_interactor; - this.plugin = plugin; - this.db = plugin.db; - - plugin.trust_manager.bad_message_state_updated.connect((account, jid, device_id) => { - clear_state(); - init_state(); - }); - } - - private void init_state() { - if (current_conversation == null) return; - if (current_conversation.type_ == Conversation.Type.GROUPCHAT_PM) return; - - var qry = db.identity_meta.select() - .join_with(db.identity, db.identity.id, db.identity_meta.identity_id) - .with(db.identity.account_id, "=", current_conversation.account.id) - .where("last_message_untrusted is not NULL OR last_message_undecryptable is not NULL"); - - switch (current_conversation.type_) { - case Conversation.Type.CHAT: - qry.with(db.identity_meta.address_name, "=", current_conversation.counterpart.to_string()); - break; - case Conversation.Type.GROUPCHAT: - bool is_private = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(current_conversation.account, current_conversation.counterpart); - if (!is_private) return; - - var list = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(current_conversation.counterpart, current_conversation.account); - if (list == null || list.is_empty) return; - - var selection = new StringBuilder(); - string[] selection_args = {}; - foreach (Jid jid in list) { - if (selection.len == 0) { - selection.append(@" ($(db.identity_meta.address_name) = ?"); - } else { - selection.append(@" OR $(db.identity_meta.address_name) = ?"); - } - selection_args += jid.to_string(); - } - selection.append(")"); - qry.where(selection.str, selection_args); - break; - case Conversation.Type.GROUPCHAT_PM: - break; - } - - foreach (Row row in qry) { - Jid jid = Jid.from_string(row[db.identity_meta.address_name]); - if (!db.identity_meta.last_message_untrusted.is_null(row)) { - DateTime time = new DateTime.from_unix_utc(row[db.identity_meta.last_message_untrusted]); - var item = new BadMessageItem(plugin, current_conversation, jid, time, BadnessType.UNTRUSTED); - bad_items.add(item); - item_collection.insert_item(item); - } - if (!db.identity_meta.last_message_undecryptable.is_null(row)) { - DateTime time = new DateTime.from_unix_utc(row[db.identity_meta.last_message_undecryptable]); - var item = new BadMessageItem(plugin, current_conversation, jid, time, BadnessType.UNDECRYPTABLE); - bad_items.add(item); - item_collection.insert_item(item); - } - } - } - - private void clear_state() { - foreach (BadMessageItem bad_item in bad_items) { - item_collection.remove_item(bad_item); - } - bad_items.clear(); - } - - public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) { - current_conversation = conversation; - this.item_collection = item_collection; - - init_state(); - } - - public void close(Conversation conversation) { - clear_state(); - } - - public void populate_timespan(Conversation conversation, DateTime after, DateTime before) { } -} - -public class BadMessageItem : Plugins.MetaConversationItem { - - private Plugin plugin; - private Conversation conversation; - private Jid problem_jid; - private BadnessType badness_type; - - public BadMessageItem(Plugin plugin, Conversation conversation, Jid jid, DateTime date, BadnessType badness_type) { - this.plugin = plugin; - this.conversation = conversation; - this.problem_jid = jid; - this.time = date; - this.badness_type = badness_type; - } - - public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) { - return new BadMessagesWidget(plugin, conversation, problem_jid, badness_type); - } - - public override Gee.List? get_item_actions(Plugins.WidgetType type) { return null; } -} - -public class BadMessagesWidget : Box { - private Plugin plugin; - private Conversation conversation; - private Jid jid; - private Label label; - - public BadMessagesWidget(Plugin plugin, Conversation conversation, Jid jid, BadnessType badness_type) { - Object(orientation:Orientation.HORIZONTAL, spacing:5); - - this.plugin = plugin; - this.conversation = conversation; - this.jid = jid; - this.halign = Align.CENTER; - this.visible = true; - - string who = ""; - if (conversation.type_ == Conversation.Type.CHAT) { - who = Dino.get_participant_display_name(plugin.app.stream_interactor, conversation, jid); - } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { - who = jid.to_string(); - // `jid` is a real JID. In MUCs, try to show nicks instead (given that the JID is currently online) - var occupants = plugin.app.stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account); - if (occupants == null) return; - foreach (Jid occupant in occupants) { - if (jid.equals_bare(plugin.app.stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(occupant, conversation.account))) { - who = occupant.resourcepart; - } - } - } - - string warning_text = ""; - if (badness_type == BadnessType.UNTRUSTED) { - warning_text = _("%s has been using an untrusted device. You won't see messages from devices that you do not trust.").printf(who) + - " %s".printf(_("Manage devices")); - } else { - warning_text += _("%s does not trust this device. That means, you might be missing messages.").printf(who); - } - label = new Label(warning_text) { margin_start=70, margin_end=70, justify=Justification.CENTER, use_markup=true, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true }; - label.add_css_class("dim-label"); - this.append(label); - - if (badness_type == BadnessType.UNTRUSTED) { - label.activate_link.connect(on_label_activate_link); - } - } - - private bool on_label_activate_link() { - var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.string("encryption")}); - GLib.Application.get_default().activate_action("open-conversation-details", variant); - return false; - } - - public override void dispose() { - if (label != null) { - label.unparent(); - label.dispose(); - label = null; - } - base.dispose(); - } -} - -} diff --git a/plugins/omemo/src/ui/call_encryption_entry.vala b/plugins/omemo/src/ui/call_encryption_entry.vala deleted file mode 100644 index 04f0443c..00000000 --- a/plugins/omemo/src/ui/call_encryption_entry.vala +++ /dev/null @@ -1,58 +0,0 @@ -using Gtk; - -using Dino.Entities; -using Dino.Qlite; -using Xmpp; - -namespace Dino.Plugins.Omemo { - - public class CallEncryptionEntry : Plugins.CallEncryptionEntry, Object { - private Database db; - - public CallEncryptionEntry(Database db) { - this.db = db; - } - - public Plugins.CallEncryptionWidget? get_widget(Account account, Xmpp.Xep.Jingle.ContentEncryption encryption) { - DtlsSrtpVerificationDraft.OmemoContentEncryption? omemo_encryption = encryption as DtlsSrtpVerificationDraft.OmemoContentEncryption; - if (omemo_encryption == null) return null; - - int identity_id = db.identity.get_id(account.id); - Row? device = db.identity_meta.get_device(identity_id, omemo_encryption.jid.to_string(), omemo_encryption.sid); - if (device == null) return null; - TrustLevel trust = (TrustLevel) device[db.identity_meta.trust_level]; - - return new CallEncryptionWidget(trust); - } - } - - public class CallEncryptionWidget : Plugins.CallEncryptionWidget, Object { - - string? title = null; - string? icon = null; - bool should_show_keys = false; - - public CallEncryptionWidget(TrustLevel trust) { - if (trust == TrustLevel.VERIFIED) { - title = "This call is encrypted and verified with OMEMO."; - icon = "dino-security-high-symbolic"; - should_show_keys = false; - } else { - title = "This call is encrypted with OMEMO."; - should_show_keys = true; - } - } - - public string? get_title() { - return title; - } - - public string? get_icon_name() { - return icon; - } - - public bool show_keys() { - return should_show_keys; - } - } -} diff --git a/plugins/omemo/src/ui/contact_details_provider.vala b/plugins/omemo/src/ui/contact_details_provider.vala deleted file mode 100644 index 1b61f31d..00000000 --- a/plugins/omemo/src/ui/contact_details_provider.vala +++ /dev/null @@ -1,28 +0,0 @@ -using Gtk; -using Gee; -using Dino.Entities; - -namespace Dino.Plugins.Omemo { - -public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object { - public string id { get { return "omemo_info"; } } - public string tab { get { return "encryption"; } } - - private Plugin plugin; - - public ContactDetailsProvider(Plugin plugin) { - this.plugin = plugin; - } - - public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) { } - - public Object? get_widget(Conversation conversation) { - if (conversation.type_ != Conversation.Type.CHAT) return null; - - var widget = new OmemoPreferencesWidget(plugin); - widget.set_jid(conversation.account, conversation.counterpart); - return widget; - } -} - -} diff --git a/plugins/omemo/src/ui/device_notification_populator.vala b/plugins/omemo/src/ui/device_notification_populator.vala deleted file mode 100644 index 7829d89c..00000000 --- a/plugins/omemo/src/ui/device_notification_populator.vala +++ /dev/null @@ -1,90 +0,0 @@ -using Dino.Entities; -using Xmpp; -using Gtk; - -namespace Dino.Plugins.Omemo { - -public class DeviceNotificationPopulator : NotificationPopulator, Object { - - public string id { get { return "device_notification"; } } - - private StreamInteractor? stream_interactor; - private Plugin plugin; - private Conversation? current_conversation; - private NotificationCollection? notification_collection; - private ConversationNotification notification; - - public DeviceNotificationPopulator(Plugin plugin, StreamInteractor stream_interactor) { - this.stream_interactor = stream_interactor; - this.plugin = plugin; - - stream_interactor.account_added.connect(on_account_added); - } - - public void init(Conversation conversation, NotificationCollection notification_collection, Plugins.WidgetType type) { - current_conversation = conversation; - this.notification_collection = notification_collection; - if (plugin.has_new_devices(conversation.account, conversation.counterpart) && conversation.type_ == Conversation.Type.CHAT) { - display_notification(); - } - } - - public void close(Conversation conversation) { - notification = null; - } - - private void display_notification() { - if (notification == null) { - notification = new ConversationNotification(plugin, current_conversation); - notification.should_hide.connect(should_hide); - notification_collection.add_meta_notification(notification); - } - } - - public void should_hide() { - if (!plugin.has_new_devices(current_conversation.account, current_conversation.counterpart) && notification != null){ - notification_collection.remove_meta_notification(notification); - notification = null; - } - } - - private void on_account_added(Account account) { - stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).bundle_fetched.connect_after((jid, device_id, bundle) => { - if (current_conversation != null && jid.equals(current_conversation.counterpart) && plugin.has_new_devices(current_conversation.account, current_conversation.counterpart)) { - display_notification(); - } - }); - } -} - -private class ConversationNotification : MetaConversationNotification { - private Widget widget; - private Plugin plugin; - private Jid jid; - private Account account; - public signal void should_hide(); - - public ConversationNotification(Plugin plugin, Conversation conversation) { - this.plugin = plugin; - this.jid = jid; - this.account = account; - - Box box = new Box(Orientation.HORIZONTAL, 5); - Button manage_button = new Button.with_label(_("Manage")); - manage_button.clicked.connect(() => { - manage_button.activate(); - - var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.string("encryption")}); - GLib.Application.get_default().activate_action("open-conversation-details", variant); - }); - box.append(new Label(_("This contact has new devices")) { margin_end=10 }); - box.append(manage_button); - widget = box; - } - - public override Object? get_widget(WidgetType type) { - return widget; - } -} - -} diff --git a/plugins/omemo/src/ui/encryption_list_entry.vala b/plugins/omemo/src/ui/encryption_list_entry.vala deleted file mode 100644 index dfec813a..00000000 --- a/plugins/omemo/src/ui/encryption_list_entry.vala +++ /dev/null @@ -1,78 +0,0 @@ -using Gtk; - -using Dino.Entities; -using Dino.Qlite; -using Xmpp; - -namespace Dino.Plugins.Omemo { - -public class EncryptionListEntry : Plugins.EncryptionListEntry, Object { - private Plugin plugin; - private Database db; - - public EncryptionListEntry(Plugin plugin) { - this.plugin = plugin; - this.db = plugin.db; - } - - public Entities.Encryption encryption { get { - return Entities.Encryption.OMEMO; - }} - - public string name { get { - return "OMEMO"; - }} - - public Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item) { - return null; - } - - public string? get_encryption_icon_name(Entities.Conversation conversation, ContentItem content_item) { - if (content_item.encryption != encryption) return null; - - RowOption row = db.content_item_meta.select( { db.identity_meta.trust_level } ).with(db.content_item_meta.content_item_id, "=", content_item.id) - .join_on(db.identity_meta, @"$(db.identity_meta.address_name)=$(db.content_item_meta.address_name) AND $(db.identity_meta.device_id)=$(db.content_item_meta.device_id)") - .single().row(); - - - if (row.is_present() && (TrustLevel) row[db.identity_meta.trust_level] == TrustLevel.VERIFIED) { - return "dino-security-high-symbolic"; - } - return null; - } - - public void encryption_activated(Entities.Conversation conversation, Plugins.SetInputFieldStatus input_status_callback) { - encryption_activated_async.begin(conversation, input_status_callback); - } - - public async void encryption_activated_async(Entities.Conversation conversation, Plugins.SetInputFieldStatus input_status_callback) { - if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) { - input_status_callback(new Plugins.InputFieldStatus("Can't use encryption in a groupchat private message.", Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); - return; - } - MucManager muc_manager = plugin.app.stream_interactor.get_module(MucManager.IDENTITY); - Manager omemo_manager = plugin.app.stream_interactor.get_module(Manager.IDENTITY); - - if (muc_manager.is_private_room(conversation.account, conversation.counterpart)) { - var offline_members = muc_manager.get_offline_members(conversation.counterpart, conversation.account); - if (offline_members == null) { - // We don't store offline members yet, and it'll be null if we're offline - return; - } - foreach (Jid offline_member in offline_members) { - bool ok = yield omemo_manager.ensure_get_keys_for_jid(conversation.account, offline_member); - if (!ok) { - input_status_callback(new Plugins.InputFieldStatus("A member does not support OMEMO: %s".printf(offline_member.to_string()), Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); - return; - } - } - return; - } - - if (!(yield omemo_manager.ensure_get_keys_for_jid(conversation.account, conversation.counterpart.bare_jid))) { - input_status_callback(new Plugins.InputFieldStatus("This contact does not support %s encryption".printf("OMEMO"), Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); - } - } - -} -} diff --git a/plugins/omemo/src/ui/encryption_preferences_entry.vala b/plugins/omemo/src/ui/encryption_preferences_entry.vala deleted file mode 100644 index cb14978b..00000000 --- a/plugins/omemo/src/ui/encryption_preferences_entry.vala +++ /dev/null @@ -1,319 +0,0 @@ -using Gee; -using Gtk; -using Qrencode; - -using Dino.Entities; -using Dino.Qlite; -using Omemo; -using Xmpp; - -namespace Dino.Plugins.Omemo { - -public class OmemoPreferencesEntry : Plugins.EncryptionPreferencesEntry { - - OmemoPreferencesWidget widget; - Plugin plugin; - - public OmemoPreferencesEntry(Plugin plugin) { - this.plugin = plugin; - } - - public override Object? get_widget(Account account, WidgetType type) { - if (type != WidgetType.GTK4) return null; - var widget = new OmemoPreferencesWidget(plugin); - widget.set_jid(account, account.bare_jid); - return widget; - } - - public override string id { get { return "omemo_preferences_entryption"; }} -} - -[GtkTemplate (ui = "/im/dino/Dino/omemo/encryption_preferences_entry.ui")] -public class OmemoPreferencesWidget : Adw.PreferencesGroup { - private Plugin plugin; - private Account account; - private Jid jid; - private int identity_id = 0; - private Store store; - private Set displayed_ids = new HashSet(); - - [GtkChild] private unowned Adw.SwitchRow automatically_accept_new_row; - [GtkChild] private unowned Adw.SwitchRow encrypt_by_default_row; - [GtkChild] private unowned Label new_keys_label; - - [GtkChild] private unowned Adw.PreferencesGroup keys_preferences_group; - [GtkChild] private unowned ListBox new_keys_listbox; - [GtkChild] private unowned Picture qrcode_picture; - [GtkChild] private unowned Popover qrcode_popover; - - private ArrayList keys_preferences_group_children = new ArrayList(); - - construct { - // If we set the strings in the .ui file, they don't get translated - encrypt_by_default_row.title = _("OMEMO by default"); - encrypt_by_default_row.subtitle = _("Enable OMEMO encryption for new conversations"); - automatically_accept_new_row.title = _("Encrypt to new devices"); - automatically_accept_new_row.subtitle = _("Automatically encrypt to new devices from this contact."); - new_keys_label.label = _("New keys"); - } - - public OmemoPreferencesWidget(Plugin plugin) { - this.plugin = plugin; - } - - public void set_jid(Account account, Jid jid) { - this.account = account; - this.jid = jid; - this.identity_id = plugin.db.identity.get_id(account.id); - if (identity_id <= 0) { - warning("OmemoPreferencesWidget missing identity_id"); - return; - } - - automatically_accept_new_row.active = plugin.db.trust.get_blind_trust(identity_id, jid.bare_jid.to_string(), true); - automatically_accept_new_row.notify["active"].connect((obj, _) => { on_auto_accept_toggled(((Adw.SwitchRow) obj).active); }); - - encrypt_by_default_row.visible = account.bare_jid.equals_bare(jid); - encrypt_by_default_row.active = plugin.app.settings.get_default_encryption(account) != Encryption.NONE; - encrypt_by_default_row.notify["active"].connect((obj, _) => { on_omemo_by_default_toggled(((Adw.SwitchRow) obj).active); }); - - Dino.Application? app = Application.get_default() as Dino.Application; - if (app != null) { - store = app.stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).store; - } - - redraw_key_list(); - - // Check for unknown devices - fetch_unknown_bundles(); - } - - private void redraw_key_list() { - // Remove current widgets - foreach (var widget in keys_preferences_group_children) { - keys_preferences_group.remove(widget); - } - keys_preferences_group_children.clear(); - - // Dialog opened from the account settings menu - // Show the fingerprint for this device separately with buttons for a qrcode and to copy - if(jid.equals(account.bare_jid)) { - automatically_accept_new_row.subtitle = _("New encryption keys from your other devices will be accepted automatically."); - add_own_fingerprint(); - } - - //Show the normal devicelist - var own_id = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; - foreach (Row device in plugin.db.identity_meta.get_known_devices(identity_id, jid.to_string())) { - if(jid.equals(account.bare_jid) && device[plugin.db.identity_meta.device_id] == own_id) { - // If this is our own account, don't show this device twice (did it separately already) - continue; - } - add_fingerprint(device, (TrustLevel) device[plugin.db.identity_meta.trust_level]); - } - - //Show any new devices for which the user must decide whether to accept or reject - foreach (Row device in plugin.db.identity_meta.get_new_devices(identity_id, jid.to_string())) { - add_new_fingerprint(device); - } - } - - private static string escape_for_iri_path_segment(string s) { - // from RFC 3986, 2.2. Reserved Characters: - string SUB_DELIMS = "!$&'()*+,;="; - // from RFC 3986, 3.3. Path (pchar without unreserved and pct-encoded): - string ALLOWED_RESERVED_CHARS = SUB_DELIMS + ":@"; - return GLib.Uri.escape_string(s, ALLOWED_RESERVED_CHARS, true); - } - - private void fetch_unknown_bundles() { - Dino.Application app = Application.get_default() as Dino.Application; - XmppStream? stream = app.stream_interactor.get_stream(account); - if (stream == null) return; - StreamModule? module = stream.get_module(StreamModule.IDENTITY); - if (module == null) return; - module.bundle_fetched.connect_after((bundle_jid, device_id, bundle) => { - if (bundle_jid.equals(jid) && !displayed_ids.contains(device_id)) { - redraw_key_list(); - } - }); - foreach (Row device in plugin.db.identity_meta.get_unknown_devices(identity_id, jid.to_string())) { - try { - module.fetch_bundle(stream, Jid.from_string(device[plugin.db.identity_meta.address_name]), device[plugin.db.identity_meta.device_id], false); - } catch (InvalidJidError e) { - warning("Ignoring device with invalid Jid: %s", e.message); - } - } - } - - private void add_own_fingerprint() { - string own_b64 = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.identity_key_public_base64]; - string fingerprint = fingerprint_from_base64(own_b64); - - var own_action_box = new Box(Orientation.HORIZONTAL, 6); - var show_qrcode_button = new MenuButton() { icon_name="dino-qr-code-symbolic", valign=Align.CENTER }; - own_action_box.append(show_qrcode_button); - var copy_button = new Button() { icon_name="dino-edit-copy-symbolic", valign=Align.CENTER }; - copy_button.clicked.connect(() => { copy_button.get_clipboard().set_text(fingerprint); }); - own_action_box.append(copy_button); - - Adw.ActionRow action_row = new Adw.ActionRow() { use_markup = true }; - action_row.title = _("This device"); - action_row.subtitle = fingerprint_markup(fingerprint_from_base64(own_b64)); - action_row.add_suffix(own_action_box); - add_key_row(action_row); - - // Create and set QR code popover - int sid = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; - var iri_query = @"omemo-sid-$(sid)=$(fingerprint)"; - string iri = GLib.Uri.join(UriFlags.NONE, "xmpp", null, null, 0, jid.to_string(), iri_query, null); - - const int QUIET_ZONE_MODULES = 4; // MUST be at least 4 - const int MODULE_SIZE_PX = 4; // arbitrary - var qr_paintable = new QRcode(iri, 2) - .to_paintable(MODULE_SIZE_PX * qrcode_picture.scale_factor); - qrcode_picture.paintable = qr_paintable; - qrcode_picture.margin_top = qrcode_picture.margin_end = - qrcode_picture.margin_bottom = qrcode_picture.margin_start = QUIET_ZONE_MODULES * MODULE_SIZE_PX; - qrcode_popover.add_css_class("qrcode-container"); - - show_qrcode_button.popover = qrcode_popover; - } - - private void add_fingerprint(Row device, TrustLevel trust) { - string key_base64 = device[plugin.db.identity_meta.identity_key_public_base64]; - bool key_active = device[plugin.db.identity_meta.now_active]; - if (store != null) { - try { - Address address = new Address(jid.to_string(), device[plugin.db.identity_meta.device_id]); - SessionRecord? session = null; - if (store.contains_session(address)) { - session = store.load_session(address); - string session_key_base64 = Base64.encode(session.state.remote_identity_key.serialize()); - if (key_base64 != session_key_base64) { - critical("Session and database identity key mismatch!"); - key_base64 = session_key_base64; - } - } - } catch (Error e) { - print("Error while reading session store: %s", e.message); - } - } - - if (device[plugin.db.identity_meta.now_active]) { - Adw.ActionRow action_row = new Adw.ActionRow() { use_markup = true }; - action_row.activated.connect(() => { - Row updated_device = plugin.db.identity_meta.get_device(device[plugin.db.identity_meta.identity_id], device[plugin.db.identity_meta.address_name], device[plugin.db.identity_meta.device_id]); - ManageKeyDialog manage_dialog = new ManageKeyDialog(updated_device, plugin.db); - manage_dialog.set_transient_for((Gtk.Window) get_root()); - manage_dialog.present(); - manage_dialog.response.connect((response) => { - update_stored_trust(response, updated_device); - redraw_key_list(); - }); - }); - action_row.activatable = true; - action_row.title = account.bare_jid.equals_bare(jid) ? _("Other device") : _("Device"); - action_row.subtitle = fingerprint_markup(fingerprint_from_base64(key_base64)); - string trust_str = _("Accepted"); - switch(trust) { - case TrustLevel.UNTRUSTED: - trust_str = _("Rejected"); - break; - case TrustLevel.VERIFIED: - trust_str = _("Verified"); - break; - } - - action_row.add_suffix(new Label(trust_str)); - add_key_row(action_row); - } - displayed_ids.add(device[plugin.db.identity_meta.device_id]); - } - - private bool on_auto_accept_toggled(bool active) { - plugin.trust_manager.set_blind_trust(account, jid, active); - - if (active) { - int identity_id = plugin.db.identity.get_id(account.id); - if (identity_id < 0) return false; - - foreach (Row device in plugin.db.identity_meta.get_new_devices(identity_id, jid.to_string())) { - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); - add_fingerprint(device, TrustLevel.TRUSTED); - } - } - return false; - } - - private bool on_omemo_by_default_toggled(bool active) { - var encryption_value = active ? Encryption.OMEMO : Encryption.NONE; - plugin.app.settings.set_default_encryption(account, encryption_value); - return false; - } - - private void update_stored_trust(int response, Row device) { - switch (response) { - case TrustLevel.TRUSTED: - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); - break; - case TrustLevel.UNTRUSTED: - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED); - break; - case TrustLevel.VERIFIED: - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.VERIFIED); - plugin.trust_manager.set_blind_trust(account, jid, false); - automatically_accept_new_row.active = false; - break; - } - } - - private void add_new_fingerprint(Row device) { - Adw.ActionRow action_row = new Adw.ActionRow() { use_markup = true }; - action_row.title = _("New device"); - action_row.subtitle = fingerprint_markup(fingerprint_from_base64(device[plugin.db.identity_meta.identity_key_public_base64])); - - Button accept_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true }; - accept_button.set_icon_name("dino-check-plain-symbolic"); // using .image = sets .image-button. Together with .suggested/destructive action that breaks the button Adwaita - accept_button.add_css_class("suggested-action"); - accept_button.tooltip_text = _("Accept key"); - - Button reject_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true }; - reject_button.set_icon_name("action-unavailable-symbolic"); - reject_button.add_css_class("destructive-action"); - reject_button.tooltip_text = _("Reject key"); - - accept_button.clicked.connect(() => { - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); - add_fingerprint(device, TrustLevel.TRUSTED); - remove_key_row(action_row); - }); - - reject_button.clicked.connect(() => { - plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED); - add_fingerprint(device, TrustLevel.UNTRUSTED); - remove_key_row(action_row); - }); - - Box control_box = new Box(Gtk.Orientation.HORIZONTAL, 0) { visible = true, hexpand = true }; - control_box.append(accept_button); - control_box.append(reject_button); - control_box.add_css_class("linked"); // .linked: Visually link the accept / reject buttons - - action_row.add_suffix(control_box); - - add_key_row(action_row); - displayed_ids.add(device[plugin.db.identity_meta.device_id]); - } - - private void add_key_row(Adw.PreferencesRow widget) { - keys_preferences_group.add(widget); - keys_preferences_group_children.add(widget); - } - - private void remove_key_row(Adw.PreferencesRow widget) { - keys_preferences_group.remove(widget); - keys_preferences_group_children.remove(widget); - } -} -} \ No newline at end of file diff --git a/plugins/omemo/src/ui/manage_key_dialog.vala b/plugins/omemo/src/ui/manage_key_dialog.vala deleted file mode 100644 index 117149b6..00000000 --- a/plugins/omemo/src/ui/manage_key_dialog.vala +++ /dev/null @@ -1,178 +0,0 @@ -using Gtk; - -using Dino.Qlite; - -namespace Dino.Plugins.Omemo { - -[GtkTemplate (ui = "/im/dino/Dino/omemo/manage_key_dialog.ui")] -public class ManageKeyDialog : Gtk.Dialog { - - [GtkChild] private unowned Stack manage_stack; - - [GtkChild] private unowned Button cancel_button; - [GtkChild] private unowned Button ok_button; - - [GtkChild] private unowned Label main_desc_label; - [GtkChild] private unowned ListBox main_action_list; - - [GtkChild] private unowned Image confirm_image; - [GtkChild] private unowned Label confirm_title_label; - [GtkChild] private unowned Label confirm_desc_label; - - [GtkChild] private unowned Label verify_label; - [GtkChild] private unowned Label compare_fingerprint_label; - [GtkChild] private unowned Button verify_yes_button; - [GtkChild] private unowned Button verify_no_button; - - private Row device; - private Database db; - - private bool return_to_main; - private int current_response; - - construct { - // If we set the strings in the .ui file, they don't get translated - this.title = _("Manage Key"); - compare_fingerprint_label.label = _("Compare the fingerprint, character by character, with the one shown on your contact's device."); - verify_no_button.label = _("Fingerprints differ"); - verify_yes_button.label = _("Fingerprints match"); - cancel_button.label = _("Cancel"); - ok_button.label = _("Confirm"); - } - - public ManageKeyDialog(Row device, Database db) { - Object(use_header_bar : 1); - - this.device = device; - this.db = db; - - setup_main_screen(); - setup_verify_screen(); - - cancel_button.clicked.connect(handle_cancel); - ok_button.clicked.connect(() => { - response(current_response); - close(); - }); - - verify_yes_button.clicked.connect(() => { - confirm_image.set_from_icon_name("security-high-symbolic"); - confirm_title_label.label = _("Verify key"); - confirm_desc_label.set_markup(_("Future messages sent by %s from the device that uses this key will be highlighted accordingly in the chat window.").printf(@"$(device[db.identity_meta.address_name])")); - manage_stack.set_visible_child_name("confirm"); - ok_button.sensitive = true; - return_to_main = false; - current_response = TrustLevel.VERIFIED; - }); - - verify_no_button.clicked.connect(() => { - return_to_main = false; - confirm_image.set_from_icon_name("dino-dialog-warning-symbolic"); - confirm_title_label.label = _("Fingerprints do not match"); - confirm_desc_label.set_markup(_("Please verify that you are comparing the correct fingerprint. If fingerprints do not match, %s's account may be compromised and you should consider rejecting this key.").printf(@"$(device[db.identity_meta.address_name])")); - manage_stack.set_visible_child_name("confirm"); - }); - } - - private void handle_cancel() { - if (manage_stack.get_visible_child_name() == "main") close(); - - if (manage_stack.get_visible_child_name() == "verify") { - manage_stack.set_visible_child_name("main"); - cancel_button.label = _("Cancel"); - } - - if (manage_stack.get_visible_child_name() == "confirm") { - if (return_to_main) { - manage_stack.set_visible_child_name("main"); - cancel_button.label = _("Cancel"); - } else { - manage_stack.set_visible_child_name("verify"); - } - } - - ok_button.sensitive = false; - } - - private Box make_action_box(string title, string desc){ - Box box = new Box(Orientation.VERTICAL, 0) { visible = true, margin_start = 20, margin_end = 20, margin_top = 14, margin_bottom = 14 }; - Label lbl_title = new Label(title) { visible = true, halign = Align.START }; - Label lbl_desc = new Label(desc) { visible = true, xalign = 0, wrap = true, max_width_chars = 40 }; - - Pango.AttrList title_attrs = new Pango.AttrList(); - title_attrs.insert(Pango.attr_scale_new(1.1)); - lbl_title.attributes = title_attrs; - Pango.AttrList desc_attrs = new Pango.AttrList(); - desc_attrs.insert(Pango.attr_scale_new(0.8)); - lbl_desc.attributes = desc_attrs; - lbl_desc.add_css_class("dim-label"); - - box.append(lbl_title); - box.append(lbl_desc); - - return box; - } - - private void setup_main_screen() { - main_action_list.set_header_func((row, before_row) => { - if (row.get_header() == null && before_row != null) { - row.set_header(new Separator(Orientation.HORIZONTAL)); - } - }); - - ListBoxRow verify_row = new ListBoxRow() { visible = true }; - verify_row.set_child(make_action_box(_("Verify key fingerprint"), _("Compare this key's fingerprint with the fingerprint displayed on the contact's device."))); - ListBoxRow reject_row = new ListBoxRow() { visible = true }; - reject_row.set_child(make_action_box(_("Reject key"), _("Block encrypted communication with the contact's device that uses this key."))); - ListBoxRow accept_row = new ListBoxRow() {visible = true }; - accept_row.set_child(make_action_box(_("Accept key"), _("Allow encrypted communication with the contact's device that uses this key."))); - - switch((TrustLevel) device[db.identity_meta.trust_level]) { - case TrustLevel.TRUSTED: - main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("accepted")+"")+" "+_("This means it can be used by %s to receive and send encrypted messages.").printf(@"$(device[db.identity_meta.address_name])")); - main_action_list.append(verify_row); - main_action_list.append(reject_row); - break; - case TrustLevel.VERIFIED: - main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("verified")+"")+" "+_("This means it can be used by %s to receive and send encrypted messages.").printf(@"$(device[db.identity_meta.address_name])") + " " + _("Additionally it has been verified to match the key on the contact's device.")); - main_action_list.append(reject_row); - break; - case TrustLevel.UNTRUSTED: - main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("rejected")+"")+" "+_("This means it cannot be used by %s to decipher your messages, and you won't see messages encrypted with it.").printf(@"$(device[db.identity_meta.address_name])")); - main_action_list.append(accept_row); - break; - } - - //Row clicked - go to appropriate screen - main_action_list.row_activated.connect((row) => { - if(row == verify_row) { - manage_stack.set_visible_child_name("verify"); - } else if (row == reject_row) { - confirm_image.set_from_icon_name("action-unavailable-symbolic"); - confirm_title_label.label = _("Reject key"); - confirm_desc_label.set_markup(_("You won't see encrypted messages from the device of %s that uses this key. Conversely, that device won't be able to decipher your messages anymore.").printf(@"$(device[db.identity_meta.address_name])")); - manage_stack.set_visible_child_name("confirm"); - ok_button.sensitive = true; - return_to_main = true; - current_response = TrustLevel.UNTRUSTED; - } else if (row == accept_row) { - confirm_image.set_from_icon_name("dino-check-plain-symbolic"); - confirm_title_label.label = _("Accept key"); - confirm_desc_label.set_markup(_("You will be able to exchange encrypted messages with the device of %s that uses this key.").printf(@"$(device[db.identity_meta.address_name])")); - manage_stack.set_visible_child_name("confirm"); - ok_button.sensitive = true; - return_to_main = true; - current_response = TrustLevel.TRUSTED; - } - cancel_button.label = _("Back"); - }); - - manage_stack.set_visible_child_name("main"); - } - - private void setup_verify_screen() { - verify_label.set_markup(fingerprint_markup(fingerprint_from_base64(device[db.identity_meta.identity_key_public_base64]))); - } -} - -} diff --git a/plugins/omemo/src/ui/own_notifications.vala b/plugins/omemo/src/ui/own_notifications.vala deleted file mode 100644 index 7ff10e30..00000000 --- a/plugins/omemo/src/ui/own_notifications.vala +++ /dev/null @@ -1,35 +0,0 @@ -using Dino.Entities; -using Xmpp; -using Gtk; - -namespace Dino.Plugins.Omemo { - -public class OwnNotifications { - - private StreamInteractor stream_interactor; - private Plugin plugin; - private Account account; - - public OwnNotifications (Plugin plugin, StreamInteractor stream_interactor, Account account) { - this.stream_interactor = (!)stream_interactor; - this.plugin = plugin; - this.account = account; - stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).bundle_fetched.connect_after((jid, device_id, bundle) => { - if (jid.equals(account.bare_jid) && plugin.has_new_devices(account, account.bare_jid)) { - display_notification(); - } - }); - - if (plugin.has_new_devices(account, account.bare_jid)) { - display_notification(); - } - } - - private void display_notification() { - Notification notification = new Notification(_("OMEMO trust decision required")); - notification.set_default_action_and_target_value("app.own-keys", new Variant.int32(account.id)); - notification.set_body(_("Did you add a new device for account %s?").printf(@"$(account.bare_jid.to_string())")); - plugin.app.send_notification(account.id.to_string()+"-new-device", notification); - } -} -} diff --git a/plugins/omemo/src/ui/util.vala b/plugins/omemo/src/ui/util.vala deleted file mode 100644 index e250ff4d..00000000 --- a/plugins/omemo/src/ui/util.vala +++ /dev/null @@ -1,40 +0,0 @@ -using Xmpp.Util; - -namespace Dino.Plugins.Omemo { - -public static string fingerprint_from_base64(string b64) { - uint8[] arr = Base64.decode(b64); - - arr = arr[1:arr.length]; - string s = ""; - foreach (uint8 i in arr) { - string tmp = i.to_string("%x"); - if (tmp.length == 1) tmp = "0" + tmp; - s = s + tmp; - } - - return s; -} - -public static string fingerprint_markup(string s) { - return "" + format_fingerprint(s) + ""; -} - -public static string format_fingerprint(string s) { - string markup = ""; - for (int i = 0; i < s.length; i += 4) { - string four_chars = s.substring(i, 4).down(); - - if (i % 32 == 0 && i != 0) markup += "\n"; - markup += four_chars; - if (i % 16 == 12 && i % 32 != 28) { - markup += " "; - } - if (i % 8 == 4 && i % 16 != 12) { - markup += "\u00a0"; // Non-breaking space - } - } - return markup; -} - -} -- cgit v1.3