Improve Plugin API (allow to move encryption into plugin)
This commit is contained in:
parent
1ccad732b9
commit
4c48bdc072
|
@ -62,7 +62,11 @@ compile_gresources(
|
||||||
|
|
||||||
vala_precompile(LIBDINO_VALA_C
|
vala_precompile(LIBDINO_VALA_C
|
||||||
SOURCES
|
SOURCES
|
||||||
src/plugin.vala
|
src/application.vala
|
||||||
|
|
||||||
|
src/plugin/interfaces.vala
|
||||||
|
src/plugin/loader.vala
|
||||||
|
src/plugin/registry.vala
|
||||||
|
|
||||||
src/dbus/login1.vala
|
src/dbus/login1.vala
|
||||||
src/dbus/networkmanager.vala
|
src/dbus/networkmanager.vala
|
||||||
|
@ -72,6 +76,7 @@ SOURCES
|
||||||
src/entity/conversation.vala
|
src/entity/conversation.vala
|
||||||
src/entity/jid.vala
|
src/entity/jid.vala
|
||||||
src/entity/message.vala
|
src/entity/message.vala
|
||||||
|
src/entity/encryption.vala
|
||||||
|
|
||||||
src/service/avatar_manager.vala
|
src/service/avatar_manager.vala
|
||||||
src/service/avatar_storage.vala
|
src/service/avatar_storage.vala
|
||||||
|
@ -101,7 +106,6 @@ SOURCES
|
||||||
src/ui/add_conversation/list_row.vala
|
src/ui/add_conversation/list_row.vala
|
||||||
src/ui/add_conversation/select_jid_fragment.vala
|
src/ui/add_conversation/select_jid_fragment.vala
|
||||||
src/ui/avatar_generator.vala
|
src/ui/avatar_generator.vala
|
||||||
src/ui/application.vala
|
|
||||||
src/ui/chat_input.vala
|
src/ui/chat_input.vala
|
||||||
src/ui/conversation_list_titlebar.vala
|
src/ui/conversation_list_titlebar.vala
|
||||||
src/ui/conversation_selector/chat_row.vala
|
src/ui/conversation_selector/chat_row.vala
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<object class="GtkPopoverMenu" id="menu_encryption">
|
<object class="GtkPopoverMenu" id="menu_encryption">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox" id="encryption_box">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
|
@ -24,22 +24,6 @@
|
||||||
<property name="position">0</property>
|
<property name="position">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="button_pgp">
|
|
||||||
<property name="label" translatable="yes">OpenPGP</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<property name="group">button_unencrypted</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="submenu">main</property>
|
<property name="submenu">main</property>
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
|
|
||||||
using Dino.Entities;
|
using Dino.Entities;
|
||||||
|
using Dino.Ui;
|
||||||
|
|
||||||
public class Dino.Ui.Application : Gtk.Application {
|
public class Dino.Application : Gtk.Application {
|
||||||
|
|
||||||
private Database db;
|
public Database db;
|
||||||
private StreamInteractor stream_interaction;
|
public StreamInteractor stream_interaction;
|
||||||
|
public Plugins.Registry plugin_registry = new Plugins.Registry();
|
||||||
|
|
||||||
private Notifications notifications;
|
private Notifications notifications;
|
||||||
private UnifiedWindow? window;
|
private UnifiedWindow? window;
|
|
@ -3,11 +3,6 @@ public class Conversation : Object {
|
||||||
|
|
||||||
public signal void object_updated(Conversation conversation);
|
public signal void object_updated(Conversation conversation);
|
||||||
|
|
||||||
public enum Encryption {
|
|
||||||
UNENCRYPTED,
|
|
||||||
PGP
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
CHAT,
|
CHAT,
|
||||||
GROUPCHAT
|
GROUPCHAT
|
||||||
|
@ -27,7 +22,7 @@ public class Conversation : Object {
|
||||||
this.account = account;
|
this.account = account;
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.last_active = new DateTime.from_unix_utc(0);
|
this.last_active = new DateTime.from_unix_utc(0);
|
||||||
this.encryption = Encryption.UNENCRYPTED;
|
this.encryption = Encryption.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Conversation.with_id(Jid jid, Account account, int id) {
|
public Conversation.with_id(Jid jid, Account account, int id) {
|
||||||
|
|
9
libdino/src/entity/encryption.vala
Normal file
9
libdino/src/entity/encryption.vala
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Dino.Entities {
|
||||||
|
|
||||||
|
public enum Encryption {
|
||||||
|
NONE,
|
||||||
|
PGP,
|
||||||
|
OMEMO
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,11 +16,6 @@ public class Dino.Entities.Message : Object {
|
||||||
WONTSEND
|
WONTSEND
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Encryption {
|
|
||||||
NONE,
|
|
||||||
PGP
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
ERROR,
|
ERROR,
|
||||||
CHAT,
|
CHAT,
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
namespace Dino {
|
|
||||||
|
|
||||||
public errordomain PluginError {
|
|
||||||
NOT_SUPPORTED,
|
|
||||||
UNEXPECTED_TYPE,
|
|
||||||
NO_REGISTRATION_FUNCTION,
|
|
||||||
FAILED
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface PluginIface : Object {
|
|
||||||
public abstract void registered(Dino.Ui.Application app);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PluginInfo : Object {
|
|
||||||
public Module module;
|
|
||||||
public Type gtype;
|
|
||||||
|
|
||||||
public PluginInfo(Type type, owned Module module) {
|
|
||||||
this.module = (owned) module;
|
|
||||||
this.gtype = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PluginLoader : Object {
|
|
||||||
[CCode (has_target = false)]
|
|
||||||
private delegate Type RegisterPluginFunction (Module module);
|
|
||||||
|
|
||||||
private PluginIface[] plugins = new PluginIface[0];
|
|
||||||
private PluginInfo[] infos = new PluginInfo[0];
|
|
||||||
|
|
||||||
public PluginIface load(string name, Dino.Ui.Application app) throws PluginError {
|
|
||||||
if (Module.supported () == false) {
|
|
||||||
throw new PluginError.NOT_SUPPORTED ("Plugins are not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
Module module = Module.open ("plugins/" + name, ModuleFlags.BIND_LAZY);
|
|
||||||
if (module == null) {
|
|
||||||
throw new PluginError.FAILED (Module.error ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void* function;
|
|
||||||
module.symbol ("register_plugin", out function);
|
|
||||||
if (function == null) {
|
|
||||||
throw new PluginError.NO_REGISTRATION_FUNCTION ("register_plugin () not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterPluginFunction register_plugin = (RegisterPluginFunction) function;
|
|
||||||
Type type = register_plugin (module);
|
|
||||||
if (type.is_a (typeof (PluginIface)) == false) {
|
|
||||||
throw new PluginError.UNEXPECTED_TYPE ("Unexpected type");
|
|
||||||
}
|
|
||||||
|
|
||||||
PluginInfo info = new PluginInfo (type, (owned) module);
|
|
||||||
infos += info;
|
|
||||||
|
|
||||||
PluginIface plugin = (PluginIface) Object.new (type);
|
|
||||||
plugins += plugin;
|
|
||||||
plugin.registered (app);
|
|
||||||
|
|
||||||
return plugin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
16
libdino/src/plugin/interfaces.vala
Normal file
16
libdino/src/plugin/interfaces.vala
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
namespace Dino.Plugins {
|
||||||
|
|
||||||
|
public interface RootInterface : Object {
|
||||||
|
public abstract void registered(Dino.Application app);
|
||||||
|
|
||||||
|
public abstract void shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface EncryptionListEntry : Object {
|
||||||
|
public abstract Entities.Encryption encryption { get; }
|
||||||
|
public abstract string name { get; }
|
||||||
|
|
||||||
|
public abstract bool can_encrypt(Entities.Conversation conversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
66
libdino/src/plugin/loader.vala
Normal file
66
libdino/src/plugin/loader.vala
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
namespace Dino.Plugins {
|
||||||
|
|
||||||
|
public errordomain Error {
|
||||||
|
NOT_SUPPORTED,
|
||||||
|
UNEXPECTED_TYPE,
|
||||||
|
NO_REGISTRATION_FUNCTION,
|
||||||
|
FAILED
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Info : Object {
|
||||||
|
public Module module;
|
||||||
|
public Type gtype;
|
||||||
|
|
||||||
|
public Info(Type type, owned Module module) {
|
||||||
|
this.module = (owned) module;
|
||||||
|
this.gtype = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Loader : Object {
|
||||||
|
[CCode (has_target = false)]
|
||||||
|
private delegate Type RegisterPluginFunction (Module module);
|
||||||
|
|
||||||
|
private RootInterface[] plugins = new RootInterface[0];
|
||||||
|
private Info[] infos = new Info[0];
|
||||||
|
|
||||||
|
public RootInterface load(string name, Dino.Application app) throws Error {
|
||||||
|
if (Module.supported () == false) {
|
||||||
|
throw new Error.NOT_SUPPORTED ("Plugins are not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
Module module = Module.open ("plugins/" + name, ModuleFlags.BIND_LAZY);
|
||||||
|
if (module == null) {
|
||||||
|
throw new Error.FAILED (Module.error ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void* function;
|
||||||
|
module.symbol ("register_plugin", out function);
|
||||||
|
if (function == null) {
|
||||||
|
throw new Error.NO_REGISTRATION_FUNCTION ("register_plugin () not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterPluginFunction register_plugin = (RegisterPluginFunction) function;
|
||||||
|
Type type = register_plugin (module);
|
||||||
|
if (type.is_a (typeof (RootInterface)) == false) {
|
||||||
|
throw new Error.UNEXPECTED_TYPE ("Unexpected type");
|
||||||
|
}
|
||||||
|
|
||||||
|
Info info = new Plugins.Info (type, (owned) module);
|
||||||
|
infos += info;
|
||||||
|
|
||||||
|
RootInterface plugin = (RootInterface) Object.new (type);
|
||||||
|
plugins += plugin;
|
||||||
|
plugin.registered (app);
|
||||||
|
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
foreach (RootInterface p in plugins) {
|
||||||
|
p.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
libdino/src/plugin/registry.vala
Normal file
20
libdino/src/plugin/registry.vala
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using Gee;
|
||||||
|
|
||||||
|
namespace Dino.Plugins {
|
||||||
|
|
||||||
|
public class Registry {
|
||||||
|
internal ArrayList<EncryptionListEntry> encryption_list_entries = new ArrayList<EncryptionListEntry>();
|
||||||
|
|
||||||
|
public bool register_encryption_list_entry(EncryptionListEntry entry) {
|
||||||
|
lock(encryption_list_entries) {
|
||||||
|
foreach(var e in encryption_list_entries) {
|
||||||
|
if (e.encryption == entry.encryption) return false;
|
||||||
|
}
|
||||||
|
encryption_list_entries.add(entry);
|
||||||
|
encryption_list_entries.sort((a,b) => b.name.collate(a.name));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -70,7 +70,7 @@ public class ConversationManager : StreamInteractionModule, Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_message_received(Entities.Message message, Conversation conversation) {
|
private void on_message_received(Entities.Message message, Xmpp.Message.Stanza message_stanza, Conversation conversation) {
|
||||||
ensure_start_conversation(conversation.counterpart, conversation.account);
|
ensure_start_conversation(conversation.counterpart, conversation.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -337,7 +337,7 @@ public class Database : Qlite.Database {
|
||||||
new_message.body = row[message.body];
|
new_message.body = row[message.body];
|
||||||
new_message.account = get_account_by_id(row[message.account_id]); // TODO dont have to generate acc new
|
new_message.account = get_account_by_id(row[message.account_id]); // TODO dont have to generate acc new
|
||||||
new_message.marked = (Message.Marked) row[message.marked];
|
new_message.marked = (Message.Marked) row[message.marked];
|
||||||
new_message.encryption = (Message.Encryption) row[message.encryption];
|
new_message.encryption = (Encryption) row[message.encryption];
|
||||||
new_message.real_jid = get_real_jid_for_message(new_message);
|
new_message.real_jid = get_real_jid_for_message(new_message);
|
||||||
|
|
||||||
new_message.notify.connect(on_message_update);
|
new_message.notify.connect(on_message_update);
|
||||||
|
@ -396,7 +396,7 @@ public class Database : Qlite.Database {
|
||||||
int64? last_active = row[conversation.last_active];
|
int64? last_active = row[conversation.last_active];
|
||||||
if (last_active != null) new_conversation.last_active = new DateTime.from_unix_utc(last_active);
|
if (last_active != null) new_conversation.last_active = new DateTime.from_unix_utc(last_active);
|
||||||
new_conversation.type_ = (Conversation.Type) row[conversation.type_];
|
new_conversation.type_ = (Conversation.Type) row[conversation.type_];
|
||||||
new_conversation.encryption = (Conversation.Encryption) row[conversation.encryption];
|
new_conversation.encryption = (Encryption) row[conversation.encryption];
|
||||||
int? read_up_to = row[conversation.read_up_to];
|
int? read_up_to = row[conversation.read_up_to];
|
||||||
if (read_up_to != null) new_conversation.read_up_to = get_message_by_id(read_up_to);
|
if (read_up_to != null) new_conversation.read_up_to = get_message_by_id(read_up_to);
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,10 @@ namespace Dino {
|
||||||
public class MessageManager : StreamInteractionModule, Object {
|
public class MessageManager : StreamInteractionModule, Object {
|
||||||
public const string ID = "message_manager";
|
public const string ID = "message_manager";
|
||||||
|
|
||||||
public signal void pre_message_received(Entities.Message message, Conversation conversation);
|
public signal void pre_message_received(Entities.Message message, Xmpp.Message.Stanza message_stanza, Conversation conversation);
|
||||||
public signal void message_received(Entities.Message message, Conversation conversation);
|
public signal void message_received(Entities.Message message, Conversation conversation);
|
||||||
|
public signal void out_message_created(Entities.Message message, Conversation conversation);
|
||||||
|
public signal void pre_message_send(Entities.Message message, Xmpp.Message.Stanza message_stanza, Conversation conversation);
|
||||||
public signal void message_sent(Entities.Message message, Conversation conversation);
|
public signal void message_sent(Entities.Message message, Conversation conversation);
|
||||||
|
|
||||||
private StreamInteractor stream_interactor;
|
private StreamInteractor stream_interactor;
|
||||||
|
@ -78,7 +80,6 @@ public class MessageManager : StreamInteractionModule, Object {
|
||||||
stream_interactor.module_manager.get_module(account, Xmpp.Message.Module.IDENTITY).received_message.connect( (stream, message) => {
|
stream_interactor.module_manager.get_module(account, Xmpp.Message.Module.IDENTITY).received_message.connect( (stream, message) => {
|
||||||
on_message_received(account, message);
|
on_message_received(account, message);
|
||||||
});
|
});
|
||||||
stream_interactor.stream_negotiated.connect(send_unsent_messages);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void send_unsent_messages(Account account) {
|
private void send_unsent_messages(Account account) {
|
||||||
|
@ -110,11 +111,8 @@ public class MessageManager : StreamInteractionModule, Object {
|
||||||
Xep.DelayedDelivery.MessageFlag? deleyed_delivery_flag = Xep.DelayedDelivery.MessageFlag.get_flag(message);
|
Xep.DelayedDelivery.MessageFlag? deleyed_delivery_flag = Xep.DelayedDelivery.MessageFlag.get_flag(message);
|
||||||
new_message.time = deleyed_delivery_flag != null ? deleyed_delivery_flag.datetime : new DateTime.now_utc();
|
new_message.time = deleyed_delivery_flag != null ? deleyed_delivery_flag.datetime : new DateTime.now_utc();
|
||||||
new_message.local_time = new DateTime.now_utc();
|
new_message.local_time = new DateTime.now_utc();
|
||||||
if (Xep.Pgp.MessageFlag.get_flag(message) != null) {
|
|
||||||
new_message.encryption = Entities.Message.Encryption.PGP;
|
|
||||||
}
|
|
||||||
Conversation conversation = ConversationManager.get_instance(stream_interactor).get_add_conversation(new_message.counterpart, account);
|
Conversation conversation = ConversationManager.get_instance(stream_interactor).get_add_conversation(new_message.counterpart, account);
|
||||||
pre_message_received(new_message, conversation);
|
pre_message_received(new_message, message, conversation);
|
||||||
|
|
||||||
bool is_uuid = new_message.stanza_id != null && Regex.match_simple("""[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}""", new_message.stanza_id);
|
bool is_uuid = new_message.stanza_id != null && Regex.match_simple("""[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}""", new_message.stanza_id);
|
||||||
if ((is_uuid && !db.contains_message_by_stanza_id(new_message.stanza_id)) ||
|
if ((is_uuid && !db.contains_message_by_stanza_id(new_message.stanza_id)) ||
|
||||||
|
@ -149,43 +147,37 @@ public class MessageManager : StreamInteractionModule, Object {
|
||||||
message.direction = Entities.Message.DIRECTION_SENT;
|
message.direction = Entities.Message.DIRECTION_SENT;
|
||||||
message.counterpart = conversation.counterpart;
|
message.counterpart = conversation.counterpart;
|
||||||
message.ourpart = new Jid(conversation.account.bare_jid.to_string() + "/" + conversation.account.resourcepart);
|
message.ourpart = new Jid(conversation.account.bare_jid.to_string() + "/" + conversation.account.resourcepart);
|
||||||
|
message.marked = Entities.Message.Marked.UNSENT;
|
||||||
|
message.encryption = conversation.encryption;
|
||||||
|
|
||||||
if (conversation.encryption == Conversation.Encryption.PGP) {
|
out_message_created(message, conversation);
|
||||||
message.encryption = Entities.Message.Encryption.PGP;
|
|
||||||
}
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void send_xmpp_message(Entities.Message message, Conversation conversation, bool delayed = false) {
|
private void send_xmpp_message(Entities.Message message, Conversation conversation, bool delayed = false) {
|
||||||
Core.XmppStream stream = stream_interactor.get_stream(conversation.account);
|
lock (messages) {
|
||||||
message.marked = Entities.Message.Marked.NONE;
|
Core.XmppStream stream = stream_interactor.get_stream(conversation.account);
|
||||||
if (stream != null) {
|
message.marked = Entities.Message.Marked.NONE;
|
||||||
Xmpp.Message.Stanza new_message = new Xmpp.Message.Stanza(message.stanza_id);
|
if (stream != null) {
|
||||||
new_message.to = message.counterpart.to_string();
|
Xmpp.Message.Stanza new_message = new Xmpp.Message.Stanza(message.stanza_id);
|
||||||
new_message.body = message.body;
|
new_message.to = message.counterpart.to_string();
|
||||||
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
|
new_message.body = message.body;
|
||||||
new_message.type_ = Xmpp.Message.Stanza.TYPE_GROUPCHAT;
|
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
|
||||||
} else {
|
new_message.type_ = Xmpp.Message.Stanza.TYPE_GROUPCHAT;
|
||||||
new_message.type_ = Xmpp.Message.Stanza.TYPE_CHAT;
|
} else {
|
||||||
}
|
new_message.type_ = Xmpp.Message.Stanza.TYPE_CHAT;
|
||||||
if (message.encryption == Entities.Message.Encryption.PGP) {
|
|
||||||
string? key_id = PgpManager.get_instance(stream_interactor).get_key_id(conversation.account, message.counterpart);
|
|
||||||
if (key_id != null) {
|
|
||||||
bool encrypted = stream.get_module(Xep.Pgp.Module.IDENTITY).encrypt(new_message, key_id);
|
|
||||||
if (!encrypted) {
|
|
||||||
message.marked = Entities.Message.Marked.WONTSEND;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
pre_message_send(message, new_message, conversation);
|
||||||
|
if (message.marked == Entities.Message.Marked.UNSENT || message.marked == Entities.Message.Marked.WONTSEND) return;
|
||||||
|
if (delayed) {
|
||||||
|
stream.get_module(Xmpp.Xep.DelayedDelivery.Module.IDENTITY).set_message_delay(new_message, message.time);
|
||||||
|
}
|
||||||
|
stream.get_module(Xmpp.Message.Module.IDENTITY).send_message(stream, new_message);
|
||||||
|
message.stanza_id = new_message.id;
|
||||||
|
message.stanza = new_message;
|
||||||
|
} else {
|
||||||
|
message.marked = Entities.Message.Marked.UNSENT;
|
||||||
}
|
}
|
||||||
if (delayed) {
|
|
||||||
stream.get_module(Xmpp.Xep.DelayedDelivery.Module.IDENTITY).set_message_delay(new_message, message.time);
|
|
||||||
}
|
|
||||||
stream.get_module(Xmpp.Message.Module.IDENTITY).send_message(stream, new_message);
|
|
||||||
message.stanza_id = new_message.id;
|
|
||||||
message.stanza = new_message;
|
|
||||||
} else {
|
|
||||||
message.marked = Entities.Message.Marked.UNSENT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,7 +161,7 @@ public class MucManager : StreamInteractionModule, Object {
|
||||||
if (stream != null) stream.get_module(Xep.Bookmarks.Module.IDENTITY).get_conferences(stream, new BookmarksRetrieveResponseListener(this, account));
|
if (stream != null) stream.get_module(Xep.Bookmarks.Module.IDENTITY).get_conferences(stream, new BookmarksRetrieveResponseListener(this, account));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_pre_message_received(Entities.Message message, Conversation conversation) {
|
private void on_pre_message_received(Entities.Message message, Xmpp.Message.Stanza message_stanza, Conversation conversation) {
|
||||||
if (conversation.type_ != Conversation.Type.GROUPCHAT) return;
|
if (conversation.type_ != Conversation.Type.GROUPCHAT) return;
|
||||||
Core.XmppStream stream = stream_interactor.get_stream(conversation.account);
|
Core.XmppStream stream = stream_interactor.get_stream(conversation.account);
|
||||||
if (stream == null) return;
|
if (stream == null) return;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Gee;
|
using Gee;
|
||||||
|
using Xmpp;
|
||||||
|
|
||||||
using Xmpp;
|
using Xmpp;
|
||||||
using Dino.Entities;
|
using Dino.Entities;
|
||||||
|
@ -16,6 +17,27 @@ namespace Dino {
|
||||||
public static void start(StreamInteractor stream_interactor, Database db) {
|
public static void start(StreamInteractor stream_interactor, Database db) {
|
||||||
PgpManager m = new PgpManager(stream_interactor, db);
|
PgpManager m = new PgpManager(stream_interactor, db);
|
||||||
stream_interactor.add_module(m);
|
stream_interactor.add_module(m);
|
||||||
|
(GLib.Application.get_default() as Application).plugin_registry.register_encryption_list_entry(new EncryptionListEntry(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EncryptionListEntry : Plugins.EncryptionListEntry, Object {
|
||||||
|
private PgpManager pgp_manager;
|
||||||
|
|
||||||
|
public EncryptionListEntry(PgpManager pgp_manager) {
|
||||||
|
this.pgp_manager = pgp_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entities.Encryption encryption { get {
|
||||||
|
return Encryption.PGP;
|
||||||
|
}}
|
||||||
|
|
||||||
|
public string name { get {
|
||||||
|
return "OpenPGP";
|
||||||
|
}}
|
||||||
|
|
||||||
|
public bool can_encrypt(Entities.Conversation conversation) {
|
||||||
|
return pgp_manager.pgp_key_ids.has_key(conversation.counterpart);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PgpManager(StreamInteractor stream_interactor, Database db) {
|
private PgpManager(StreamInteractor stream_interactor, Database db) {
|
||||||
|
@ -23,6 +45,27 @@ namespace Dino {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
|
|
||||||
stream_interactor.account_added.connect(on_account_added);
|
stream_interactor.account_added.connect(on_account_added);
|
||||||
|
MessageManager.get_instance(stream_interactor).pre_message_received.connect(on_pre_message_received);
|
||||||
|
MessageManager.get_instance(stream_interactor).pre_message_send.connect(on_pre_message_send);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_pre_message_received(Entities.Message message, Xmpp.Message.Stanza message_stanza, Conversation conversation) {
|
||||||
|
if (Xep.Pgp.MessageFlag.get_flag(message_stanza) != null && Xep.Pgp.MessageFlag.get_flag(message_stanza).decrypted) {
|
||||||
|
message.encryption = Encryption.PGP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_pre_message_send(Entities.Message message, Xmpp.Message.Stanza message_stanza, Conversation conversation) {
|
||||||
|
if (message.encryption == Encryption.PGP) {
|
||||||
|
string? key_id = get_key_id(conversation.account, message.counterpart);
|
||||||
|
bool encrypted = false;
|
||||||
|
if (key_id != null) {
|
||||||
|
encrypted = stream_interactor.get_stream(conversation.account).get_module(Xep.Pgp.Module.IDENTITY).encrypt(message_stanza, key_id);
|
||||||
|
}
|
||||||
|
if (!encrypted) {
|
||||||
|
message.marked = Entities.Message.Marked.WONTSEND;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? get_key_id(Account account, Jid jid) {
|
public string? get_key_id(Account account, Jid jid) {
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class StreamInteractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface StreamInteractionModule : Object {
|
public interface StreamInteractionModule : Object {
|
||||||
internal abstract string get_id();
|
public abstract string get_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -33,7 +33,7 @@ public class MergedMessageItem : Grid {
|
||||||
string display_name = Util.get_message_display_name(stream_interactor, message, conversation.account);
|
string display_name = Util.get_message_display_name(stream_interactor, message, conversation.account);
|
||||||
name_label.set_markup(@"<span foreground=\"#$(Util.get_name_hex_color(display_name))\">$display_name</span>");
|
name_label.set_markup(@"<span foreground=\"#$(Util.get_name_hex_color(display_name))\">$display_name</span>");
|
||||||
Util.image_set_from_scaled_pixbuf(image, (new AvatarGenerator(30, 30, image.scale_factor)).draw_message(stream_interactor, message));
|
Util.image_set_from_scaled_pixbuf(image, (new AvatarGenerator(30, 30, image.scale_factor)).draw_message(stream_interactor, message));
|
||||||
if (message.encryption == Entities.Message.Encryption.PGP) {
|
if (message.encryption != Encryption.NONE) {
|
||||||
encryption_image.visible = true;
|
encryption_image.visible = true;
|
||||||
encryption_image.set_from_icon_name("changes-prevent-symbolic", IconSize.SMALL_TOOLBAR);
|
encryption_image.set_from_icon_name("changes-prevent-symbolic", IconSize.SMALL_TOOLBAR);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
|
using Gee;
|
||||||
|
|
||||||
using Dino.Entities;
|
using Dino.Entities;
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ public class ConversationTitlebar : Gtk.HeaderBar {
|
||||||
[GtkChild] private MenuButton groupchat_button;
|
[GtkChild] private MenuButton groupchat_button;
|
||||||
|
|
||||||
private RadioButton? button_unencrypted;
|
private RadioButton? button_unencrypted;
|
||||||
private RadioButton? button_pgp;
|
private Map<RadioButton, Plugins.EncryptionListEntry> encryption_radios = new HashMap<RadioButton, Plugins.EncryptionListEntry>();
|
||||||
|
|
||||||
private StreamInteractor stream_interactor;
|
private StreamInteractor stream_interactor;
|
||||||
private Conversation? conversation;
|
private Conversation? conversation;
|
||||||
|
@ -36,22 +37,19 @@ public class ConversationTitlebar : Gtk.HeaderBar {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update_encryption_menu_state() {
|
private void update_encryption_menu_state() {
|
||||||
string? pgp_id = PgpManager.get_instance(stream_interactor).get_key_id(conversation.account, conversation.counterpart);
|
foreach (RadioButton e in encryption_radios.keys) {
|
||||||
button_pgp.set_sensitive(pgp_id != null);
|
e.set_sensitive(encryption_radios[e].can_encrypt(conversation));
|
||||||
switch (conversation.encryption) {
|
if (conversation.encryption == encryption_radios[e].encryption) e.set_active(true);
|
||||||
case Conversation.Encryption.UNENCRYPTED:
|
}
|
||||||
button_unencrypted.set_active(true);
|
if (conversation.encryption == Encryption.NONE) {
|
||||||
break;
|
button_unencrypted.set_active(true);
|
||||||
case Conversation.Encryption.PGP:
|
|
||||||
button_pgp.set_active(true);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update_encryption_menu_icon() {
|
private void update_encryption_menu_icon() {
|
||||||
encryption_button.visible = (conversation.type_ == Conversation.Type.CHAT);
|
encryption_button.visible = (conversation.type_ == Conversation.Type.CHAT);
|
||||||
if (conversation.type_ == Conversation.Type.CHAT) {
|
if (conversation.type_ == Conversation.Type.CHAT) {
|
||||||
if (conversation.encryption == Conversation.Encryption.UNENCRYPTED) {
|
if (conversation.encryption == Encryption.NONE) {
|
||||||
encryption_button.set_image(new Image.from_icon_name("changes-allow-symbolic", IconSize.BUTTON));
|
encryption_button.set_image(new Image.from_icon_name("changes-allow-symbolic", IconSize.BUTTON));
|
||||||
} else {
|
} else {
|
||||||
encryption_button.set_image(new Image.from_icon_name("changes-prevent-symbolic", IconSize.BUTTON));
|
encryption_button.set_image(new Image.from_icon_name("changes-prevent-symbolic", IconSize.BUTTON));
|
||||||
|
@ -92,25 +90,35 @@ public class ConversationTitlebar : Gtk.HeaderBar {
|
||||||
menu_button.set_menu_model(menu);
|
menu_button.set_menu_model(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void encryption_changed() {
|
||||||
|
foreach (RadioButton e in encryption_radios.keys) {
|
||||||
|
if (e.get_active()) {
|
||||||
|
conversation.encryption = encryption_radios[e].encryption;
|
||||||
|
update_encryption_menu_icon();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conversation.encryption = Encryption.NONE;
|
||||||
|
update_encryption_menu_icon();
|
||||||
|
}
|
||||||
|
|
||||||
private void create_encryption_menu() {
|
private void create_encryption_menu() {
|
||||||
Builder builder = new Builder.from_resource("/org/dino-im/menu_encryption.ui");
|
Builder builder = new Builder.from_resource("/org/dino-im/menu_encryption.ui");
|
||||||
PopoverMenu menu = builder.get_object("menu_encryption") as PopoverMenu;
|
PopoverMenu menu = builder.get_object("menu_encryption") as PopoverMenu;
|
||||||
|
Box encryption_box = builder.get_object("encryption_box") as Box;
|
||||||
button_unencrypted = builder.get_object("button_unencrypted") as RadioButton;
|
button_unencrypted = builder.get_object("button_unencrypted") as RadioButton;
|
||||||
button_pgp = builder.get_object("button_pgp") as RadioButton;
|
button_unencrypted.toggled.connect(encryption_changed);
|
||||||
|
Application app = GLib.Application.get_default() as Application;
|
||||||
|
foreach(var e in app.plugin_registry.encryption_list_entries) {
|
||||||
|
RadioButton btn = new RadioButton.with_label(button_unencrypted.get_group(), e.name);
|
||||||
|
encryption_radios[btn] = e;
|
||||||
|
btn.toggled.connect(encryption_changed);
|
||||||
|
btn.visible = true;
|
||||||
|
encryption_box.pack_end(btn, false);
|
||||||
|
}
|
||||||
encryption_button.set_use_popover(true);
|
encryption_button.set_use_popover(true);
|
||||||
encryption_button.set_popover(menu);
|
encryption_button.set_popover(menu);
|
||||||
encryption_button.set_image(new Image.from_icon_name("changes-allow-symbolic", IconSize.BUTTON));
|
encryption_button.set_image(new Image.from_icon_name("changes-allow-symbolic", IconSize.BUTTON));
|
||||||
|
|
||||||
button_unencrypted.toggled.connect(() => {
|
|
||||||
if (conversation != null) {
|
|
||||||
if (button_unencrypted.get_active()) {
|
|
||||||
conversation.encryption = Conversation.Encryption.UNENCRYPTED;
|
|
||||||
} else if (button_pgp.get_active()) {
|
|
||||||
conversation.encryption = Conversation.Encryption.PGP;
|
|
||||||
}
|
|
||||||
update_encryption_menu_icon();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_groupchat_subject_set(Account account, Jid jid, string subject) {
|
private void on_groupchat_subject_set(Account account, Jid jid, string subject) {
|
||||||
|
|
|
@ -5,16 +5,17 @@ namespace Dino {
|
||||||
|
|
||||||
void main(string[] args) {
|
void main(string[] args) {
|
||||||
Gtk.init(ref args);
|
Gtk.init(ref args);
|
||||||
Dino.Ui.Application app = new Dino.Ui.Application();
|
Application app = new Application();
|
||||||
PluginLoader loader = new PluginLoader();
|
Plugins.Loader loader = new Plugins.Loader();
|
||||||
foreach(string plugin in new string[]{}) {
|
foreach(string plugin in new string[]{}) {
|
||||||
try {
|
try {
|
||||||
loader.load(plugin, app);
|
loader.load(plugin, app);
|
||||||
} catch (Dino.PluginError e) {
|
} catch (Plugins.Error e) {
|
||||||
print(@"Error loading plugin $plugin: $(e.message)\n");
|
print(@"Error loading plugin $plugin: $(e.message)\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.run(args);
|
app.run(args);
|
||||||
|
loader.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
namespace Xmpp {
|
namespace Xmpp {
|
||||||
string? get_bare_jid(string jid) {
|
public string? get_bare_jid(string jid) {
|
||||||
return jid.split("/")[0];
|
return jid.split("/")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_bare_jid(string jid) {
|
public bool is_bare_jid(string jid) {
|
||||||
return !jid.contains("/");
|
return !jid.contains("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
string? get_resource_part(string jid) {
|
public string? get_resource_part(string jid) {
|
||||||
return jid.split("/")[1];
|
return jid.split("/")[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue