diff options
-rw-r--r-- | libdino/src/service/database.vala | 25 | ||||
-rw-r--r-- | plugins/gpgme-vala/src/gpgme_helper.vala | 13 | ||||
-rw-r--r-- | plugins/openpgp/CMakeLists.txt | 5 | ||||
-rw-r--r-- | plugins/openpgp/data/account_settings_item.ui | 6 | ||||
-rw-r--r-- | plugins/openpgp/src/account_settings_entry.vala | 8 | ||||
-rw-r--r-- | plugins/openpgp/src/account_settings_widget.vala | 100 | ||||
-rw-r--r-- | plugins/openpgp/src/database.vala | 67 | ||||
-rw-r--r-- | plugins/openpgp/src/manager.vala | 4 | ||||
-rw-r--r-- | plugins/openpgp/src/plugin.vala | 26 | ||||
-rw-r--r-- | plugins/openpgp/src/stream_flag.vala (renamed from plugins/openpgp/src/xmpp_flag.vala) | 0 | ||||
-rw-r--r-- | plugins/openpgp/src/stream_module.vala (renamed from plugins/openpgp/src/xmpp_module.vala) | 32 |
11 files changed, 201 insertions, 85 deletions
diff --git a/libdino/src/service/database.vala b/libdino/src/service/database.vala index 83686424..b50581f8 100644 --- a/libdino/src/service/database.vala +++ b/libdino/src/service/database.vala @@ -103,16 +103,6 @@ public class Database : Qlite.Database { } } - public class PgpTable : Table { - public Column<string> jid = new Column.Text("jid") { primary_key = true }; - public Column<string> key = new Column.Text("key") { not_null = true }; - - protected PgpTable(Database db) { - base(db, "pgp"); - init({jid, key}); - } - } - public class EntityFeatureTable : Table { public Column<string> entity = new Column.Text("entity"); public Column<string> feature = new Column.Text("feature"); @@ -129,7 +119,6 @@ public class Database : Qlite.Database { public RealJidTable real_jid { get; private set; } public ConversationTable conversation { get; private set; } public AvatarTable avatar { get; private set; } - public PgpTable pgp { get; private set; } public EntityFeatureTable entity_feature { get; private set; } public Database(string fileName) { @@ -140,9 +129,8 @@ public class Database : Qlite.Database { real_jid = new RealJidTable(this); conversation = new ConversationTable(this); avatar = new AvatarTable(this); - pgp = new PgpTable(this); entity_feature = new EntityFeatureTable(this); - init({ account, jid, message, real_jid, conversation, avatar, pgp, entity_feature }); + init({ account, jid, message, real_jid, conversation, avatar, entity_feature }); } public override void migrate(long oldVersion) { @@ -420,17 +408,6 @@ public class Database : Qlite.Database { return ret; } - public void set_pgp_key(Jid jid, string key) { - pgp.insert().or("REPLACE") - .value(pgp.jid, jid.to_string()) - .value(pgp.key, key) - .perform(); - } - - public string? get_pgp_key(Jid jid) { - return pgp.select({pgp.key}).with(pgp.jid, "=", jid.to_string())[pgp.key]; - } - public void add_entity_features(string entity, ArrayList<string> features) { foreach (string feature in features) { entity_feature.insert() diff --git a/plugins/gpgme-vala/src/gpgme_helper.vala b/plugins/gpgme-vala/src/gpgme_helper.vala index 9efa2b4c..429c96f0 100644 --- a/plugins/gpgme-vala/src/gpgme_helper.vala +++ b/plugins/gpgme-vala/src/gpgme_helper.vala @@ -28,12 +28,13 @@ public static string decrypt(string encr) throws GLib.Error { return get_string_from_data(dec_data); } -public static string sign(string plain, SigMode mode) throws GLib.Error { +public static string sign(string plain, SigMode mode, Key? key = null) throws GLib.Error { initialize(); global_mutex.lock(); Data plain_data = Data.create_from_memory(plain.data, false); Context context = Context.create(); + if (key != null) context.signers_add(key); Data signed_data = context.op_sign(plain_data, mode); global_mutex.unlock(); return get_string_from_data(signed_data); @@ -76,11 +77,19 @@ public static Gee.List<Key> get_keylist(string? pattern = null, bool secret_only } public static Key? get_public_key(string sig) throws GLib.Error { + return get_key(sig, false); +} + +public static Key? get_private_key(string sig) throws GLib.Error { + return get_key(sig, true); +} + +private static Key? get_key(string sig, bool priv) throws GLib.Error { initialize(); global_mutex.lock(); Context context = Context.create(); - Key key = context.get_key(sig, false); + Key key = context.get_key(sig, priv); global_mutex.unlock(); return key; } diff --git a/plugins/openpgp/CMakeLists.txt b/plugins/openpgp/CMakeLists.txt index a230872e..09a4ca7f 100644 --- a/plugins/openpgp/CMakeLists.txt +++ b/plugins/openpgp/CMakeLists.txt @@ -31,12 +31,13 @@ vala_precompile(OPENPGP_VALA_C SOURCES src/account_settings_entry.vala src/account_settings_widget.vala + src/database.vala src/encryption_list_entry.vala src/manager.vala src/plugin.vala src/register_plugin.vala - src/xmpp_flag.vala - src/xmpp_module.vala + src/stream_flag.vala + src/stream_module.vala CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/gpgme.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi diff --git a/plugins/openpgp/data/account_settings_item.ui b/plugins/openpgp/data/account_settings_item.ui index 95f09046..299b9a89 100644 --- a/plugins/openpgp/data/account_settings_item.ui +++ b/plugins/openpgp/data/account_settings_item.ui @@ -3,11 +3,11 @@ <template class="DinoPluginsOpenPgpAccountSettingsWidget"> <property name="visible">True</property> <child> - <object class="GtkButton" id="pgp_button"> + <object class="GtkButton" id="button"> <property name="relief">none</property> <property name="visible">True</property> <child> - <object class="GtkLabel" id="pgp_label"> + <object class="GtkLabel" id="label"> <property name="xalign">0</property> <property name="visible">True</property> </object> @@ -18,7 +18,7 @@ </packing> </child> <child> - <object class="GtkComboBox" id="pgp_combobox"> + <object class="GtkComboBox" id="combobox"> <property name="hexpand">True</property> <property name="width_request">200</property> <property name="visible">True</property> diff --git a/plugins/openpgp/src/account_settings_entry.vala b/plugins/openpgp/src/account_settings_entry.vala index 1deef763..c7d11d72 100644 --- a/plugins/openpgp/src/account_settings_entry.vala +++ b/plugins/openpgp/src/account_settings_entry.vala @@ -2,6 +2,12 @@ namespace Dino.Plugins.OpenPgp { public class AccountSettingsEntry : Plugins.AccountSettingsEntry { + private Plugin plugin; + + public AccountSettingsEntry(Plugin plugin) { + this.plugin = plugin; + } + public override string id { get { return "pgp_key_picker"; }} @@ -11,7 +17,7 @@ public class AccountSettingsEntry : Plugins.AccountSettingsEntry { }} public override Plugins.AccountSettingsWidget get_widget() { - return new AccountSettingsWidget(); + return new AccountSettingsWidget(plugin); } } diff --git a/plugins/openpgp/src/account_settings_widget.vala b/plugins/openpgp/src/account_settings_widget.vala index b9e6edbd..71739096 100644 --- a/plugins/openpgp/src/account_settings_widget.vala +++ b/plugins/openpgp/src/account_settings_widget.vala @@ -1,60 +1,96 @@ +using Gtk; + using Dino.Entities; namespace Dino.Plugins.OpenPgp { [GtkTemplate (ui = "/org/dino-im/account_settings_item.ui")] -private class AccountSettingsWidget : Gtk.Stack, Plugins.AccountSettingsWidget { - [GtkChild] private Gtk.Label pgp_label; - [GtkChild] private Gtk.Button pgp_button; - [GtkChild] private Gtk.ComboBox pgp_combobox; +private class AccountSettingsWidget : Stack, Plugins.AccountSettingsWidget { + [GtkChild] private Label label; + [GtkChild] private Button button; + [GtkChild] private ComboBox combobox; + private Plugin plugin; + private Account current_account; private Gtk.ListStore list_store = new Gtk.ListStore(2, typeof(string), typeof(string?)); - public AccountSettingsWidget() { - Gtk.CellRendererText renderer = new Gtk.CellRendererText(); + public AccountSettingsWidget(Plugin plugin) { + this.plugin = plugin; + + CellRendererText renderer = new CellRendererText(); renderer.set_padding(0, 0); - pgp_combobox.pack_start(renderer, true); - pgp_combobox.add_attribute(renderer, "markup", 0); - pgp_button.clicked.connect(() => { activated(); this.set_visible_child_name("entry"); pgp_combobox.popup(); }); + combobox.pack_start(renderer, true); + combobox.add_attribute(renderer, "markup", 0); + + button.clicked.connect(on_button_clicked); + combobox.changed.connect(key_changed); } public void deactivate() { this.set_visible_child_name("label"); } - private void key_changed() { - Gtk.TreeIter selected; - pgp_combobox.get_active_iter(out selected); - Value text; - list_store.get_value(selected, 0, out text); - pgp_label.set_markup((string) text); - deactivate(); - } - public void set_account(Account account) { - populate_pgp_combobox(account); + this.current_account = account; + populate(account); } - private void populate_pgp_combobox(Account account) { - pgp_combobox.changed.disconnect(key_changed); + private void on_button_clicked() { + activated(); + this.set_visible_child_name("entry"); + combobox.popup(); + } - Gtk.TreeIter iter; - pgp_combobox.set_model(list_store); + private void populate(Account account) { + TreeIter iter; + combobox.set_model(list_store); list_store.clear(); - list_store.append(out iter); - pgp_label.set_markup("Disabled\n<span font='9'>Select key</span>"); - list_store.set(iter, 0, "Disabled\n<span font='9'>Select key</span>", 1, null); - Gee.List<GPG.Key> list = GPGHelper.get_keylist(null, true); - foreach (GPG.Key key in list) { + try { + Gee.List<GPG.Key> keys = GPGHelper.get_keylist(null, true); + + list_store.append(out iter); + list_store.set(iter, 0, "Disabled\n<span font='9'>Select key</span>", 1, null); + set_label_active(iter, 0); + for (int i = 0; i < keys.size; i++) { + list_store.append(out iter); + string text = @"<span font='11'>$(Markup.escape_text(keys[i].uids[0].uid))</span>\n<span font='9'>0x$(Markup.escape_text(keys[i].fpr[0:16]))</span>"; + list_store.set(iter, 0, text); + list_store.set(iter, 1, keys[i].fpr); + if (keys[i].fpr == plugin.db.get_account_key(account)) { + set_label_active(iter, i + 1); + } + } + } catch (Error e){ list_store.append(out iter); - list_store.set(iter, 0, @"<span font='11'>$(Markup.escape_text(key.uids[0].uid))</span>\n<span font='9'>0x$(Markup.escape_text(key.fpr[0:16]))</span>"); - list_store.set(iter, 1, key.fpr); + list_store.set(iter, 0, @"Disabled\n<span font='9'>Error: $(Markup.escape_text(e.message))</span>", 1, null); } + } + + private void set_label_active(TreeIter iter, int i = -1) { + Value text; + list_store.get_value(iter, 0, out text); + label.set_markup((string) text); + if (i != -1) combobox.active = i; + } - pgp_combobox.set_active(0); - pgp_combobox.changed.connect(key_changed); + private void key_changed() { + TreeIter selected; + bool iter_valid = combobox.get_active_iter(out selected); + if (iter_valid) { + Value key_value; + list_store.get_value(selected, 1, out key_value); + string? key_id = key_value as string; + if (key_id != null) { + if (plugin.modules.has_key(current_account)) { + plugin.modules[current_account].set_private_key_id(key_id); + } + plugin.db.set_account_key(current_account, key_id); + } + set_label_active(selected); + deactivate(); + } } } diff --git a/plugins/openpgp/src/database.vala b/plugins/openpgp/src/database.vala new file mode 100644 index 00000000..ac80f79a --- /dev/null +++ b/plugins/openpgp/src/database.vala @@ -0,0 +1,67 @@ +using Qlite; + +using Dino.Entities; + +namespace Dino.Plugins.OpenPgp { + +public class Database : Qlite.Database { + private const int VERSION = 0; + + public class AccountSetting : Table { + public Column<int> account_id = new Column.Integer("account_id") { primary_key = true }; + public Column<string> key = new Column.Text("key") { not_null = true }; + + protected AccountSetting(Database db) { + base(db, "account_setting"); + init({account_id, key}); + } + } + + public class ContactKey : Table { + public Column<string> jid = new Column.Text("jid") { primary_key = true }; + public Column<string> key = new Column.Text("key") { not_null = true }; + + protected ContactKey(Database db) { + base(db, "contact_key"); + init({jid, key}); + } + } + + public AccountSetting account_setting_table { get; private set; } + public ContactKey contact_key_table { get; private set; } + + public Database(string filename) { + base(filename, VERSION); + this.account_setting_table = new AccountSetting(this); + this.contact_key_table = new ContactKey(this); + init({account_setting_table, contact_key_table}); + } + + public void set_contact_key(Jid jid, string key) { + contact_key_table.insert().or("REPLACE") + .value(contact_key_table.jid, jid.to_string()) + .value(contact_key_table.key, key) + .perform(); + } + + public string? get_contact_key(Jid jid) { + return contact_key_table.select({contact_key_table.key}) + .with(contact_key_table.jid, "=", jid.bare_jid.to_string())[contact_key_table.key]; + } + + public void set_account_key(Account account, string key) { + account_setting_table.insert().or("REPLACE") + .value(account_setting_table.account_id, account.id) + .value(account_setting_table.key, key) + .perform(); + } + + public string? get_account_key(Account account) { + return account_setting_table.select({account_setting_table.key}) + .with(account_setting_table.account_id, "=", account.id)[account_setting_table.key]; + } + + public override void migrate(long oldVersion) { } +} + +}
\ No newline at end of file diff --git a/plugins/openpgp/src/manager.vala b/plugins/openpgp/src/manager.vala index 81077088..4fe50939 100644 --- a/plugins/openpgp/src/manager.vala +++ b/plugins/openpgp/src/manager.vala @@ -49,7 +49,7 @@ namespace Dino.Plugins.OpenPgp { } public string? get_key_id(Account account, Jid jid) { - return db.get_pgp_key(jid); + return db.get_contact_key(jid); } public static Manager? get_instance(StreamInteractor stream_interactor) { @@ -69,7 +69,7 @@ namespace Dino.Plugins.OpenPgp { private void on_jid_key_received(Account account, Jid jid, string key_id) { if (!pgp_key_ids.has_key(jid) || pgp_key_ids[jid] != key_id) { if (!MucManager.get_instance(stream_interactor).is_groupchat_occupant(jid, account)) { - db.set_pgp_key(jid.bare_jid, key_id); + db.set_contact_key(jid.bare_jid, key_id); } } pgp_key_ids[jid] = key_id; diff --git a/plugins/openpgp/src/plugin.vala b/plugins/openpgp/src/plugin.vala index d25c8cd0..b69f7b7a 100644 --- a/plugins/openpgp/src/plugin.vala +++ b/plugins/openpgp/src/plugin.vala @@ -1,28 +1,36 @@ +using Gee; + +using Dino.Entities; + namespace Dino.Plugins.OpenPgp { public class Plugin : Plugins.RootInterface, Object { public Dino.Application app; public Database db; + public HashMap<Account, Module> modules = new HashMap<Account, Module>(Account.hash_func, Account.equals_func); - private Module module; private EncryptionListEntry list_entry; private AccountSettingsEntry settings_entry; public void registered(Dino.Application app) { this.app = app; - this.module = new Module(); + this.db = new Database(Path.build_filename(Application.get_storage_dir(), "pgp.db")); this.list_entry = new EncryptionListEntry(app.stream_interaction); - this.settings_entry = new AccountSettingsEntry(); + this.settings_entry = new AccountSettingsEntry(this); + app.plugin_registry.register_encryption_list_entry(list_entry); app.plugin_registry.register_account_settings_entry(settings_entry); - app.stream_interaction.module_manager.initialize_account_modules.connect((account, list) => { - list.add(new Module()); - }); - Manager.start(app.stream_interaction, app.db); + app.stream_interaction.module_manager.initialize_account_modules.connect(on_initialize_account_modules); + + Manager.start(app.stream_interaction, db); } - public void shutdown() { - // Nothing to do + public void shutdown() { } + + private void on_initialize_account_modules(Account account, ArrayList<Xmpp.Core.XmppStreamModule> modules) { + Module module = new Module(db.get_account_key(account)); + this.modules[account] = module; + modules.add(module); } } diff --git a/plugins/openpgp/src/xmpp_flag.vala b/plugins/openpgp/src/stream_flag.vala index 5ace26bd..5ace26bd 100644 --- a/plugins/openpgp/src/xmpp_flag.vala +++ b/plugins/openpgp/src/stream_flag.vala diff --git a/plugins/openpgp/src/xmpp_module.vala b/plugins/openpgp/src/stream_module.vala index 440be5f1..6de97066 100644 --- a/plugins/openpgp/src/xmpp_module.vala +++ b/plugins/openpgp/src/stream_module.vala @@ -14,16 +14,28 @@ namespace Dino.Plugins.OpenPgp { public signal void received_jid_key_id(XmppStream stream, string jid, string key_id); - private string? signed_status; - private string? own_key_id; + private string? signed_status = null; + private Key? own_key = null; - public Module() { - signed_status = gpg_sign(""); - if (signed_status != null) own_key_id = gpg_verify(signed_status, ""); + public Module(string? own_key_id = null) { + set_private_key_id(own_key_id); + } + + public void set_private_key_id(string? own_key_id) { + if (own_key_id != null) { + try { + own_key = GPGHelper.get_private_key(own_key_id); + if (own_key == null) print("PRIV KEY NULL\n"); + } catch (Error e) { } + if (own_key != null) { + signed_status = gpg_sign("", own_key); + get_sign_key(signed_status, ""); + } + } } public bool encrypt(Message.Stanza message, string key_id) { - string? enc_body = gpg_encrypt(message.body, new string[] {key_id, own_key_id}); + string? enc_body = gpg_encrypt(message.body, new string[] {key_id, own_key.fpr}); if (enc_body != null) { message.stanza.put_node(new StanzaNode.build("x", NS_URI_ENCRYPTED).add_self_xmlns().put_node(new StanzaNode.text(enc_body))); message.body = "[This message is OpenPGP encrypted (see XEP-0027)]"; @@ -65,7 +77,7 @@ namespace Dino.Plugins.OpenPgp { string? sig = x_node.get_string_content(); if (sig != null) { string signed_data = presence.status == null ? "" : presence.status; - string? key_id = gpg_verify(sig, signed_data); + string? key_id = get_sign_key(sig, signed_data); if (key_id != null) { Flag.get_flag(stream).set_key_id(presence.from, key_id); received_jid_key_id(stream, presence.from, key_id); @@ -117,7 +129,7 @@ namespace Dino.Plugins.OpenPgp { return decr; } - private static string? gpg_verify(string sig, string signed_text) { + private static string? get_sign_key(string sig, string signed_text) { string armor = "-----BEGIN PGP MESSAGE-----\n\n" + sig + "\n-----END PGP MESSAGE-----"; string? sign_key = null; try { @@ -126,10 +138,10 @@ namespace Dino.Plugins.OpenPgp { return sign_key; } - private static string? gpg_sign(string str) { + private static string? gpg_sign(string str, Key key) { string signed; try { - signed = GPGHelper.sign(str, GPG.SigMode.CLEAR); + signed = GPGHelper.sign(str, GPG.SigMode.CLEAR, key); } catch (Error e) { return null; } |