diff --git a/libdino/src/application.vala b/libdino/src/application.vala index ad19a976..4b5fd274 100644 --- a/libdino/src/application.vala +++ b/libdino/src/application.vala @@ -8,8 +8,17 @@ public class Dino.Application : Gtk.Application { public StreamInteractor stream_interaction; public Plugins.Registry plugin_registry = new Plugins.Registry(); - public Application() { - this.db = new Database("store.sqlite3"); + public Application() throws Error { + if (DirUtils.create_with_parents(get_storage_dir(), 0700) == -1) { + throw new Error(-1, 0, @"Could not create storage dir \"$(get_storage_dir())\": $(FileUtils.error_from_errno(errno))"); + } + + // FIXME: Legacy import + if (FileUtils.test("store.sqlite3", FileTest.IS_REGULAR)) { + FileUtils.rename("store.sqlite3", Path.build_filename(get_storage_dir(), "dino.db")); + } + + this.db = new Database(Path.build_filename(get_storage_dir(), "dino.db")); this.stream_interaction = new StreamInteractor(db); AvatarManager.start(stream_interaction, db); @@ -21,5 +30,9 @@ public class Dino.Application : Gtk.Application { ConversationManager.start(stream_interaction, db); ChatInteraction.start(stream_interaction); } + + public static string get_storage_dir() { + return Path.build_filename(Environment.get_user_data_dir(), "dino"); + } } diff --git a/libdino/src/plugin/loader.vala b/libdino/src/plugin/loader.vala index acb26ff4..42d7fa9b 100644 --- a/libdino/src/plugin/loader.vala +++ b/libdino/src/plugin/loader.vala @@ -14,17 +14,40 @@ public class Loader : Object { [CCode (has_target = false)] private delegate Type RegisterPluginFunction (Module module); + private string[] search_paths = new string[0]; private RootInterface[] plugins = new RootInterface[0]; private Info[] infos = new Info[0]; + public Loader(string? exec_str = null) { + search_paths += Application.get_storage_dir(); + if (exec_str != null) { + string exec_path = exec_str; + if (!exec_path.contains(Path.DIR_SEPARATOR_S)) { + exec_path = Environment.find_program_in_path(exec_str); + } + if (exec_path[0:5] != "/usr") { + search_paths += Path.get_dirname(exec_path); + } + } + foreach (string dir in Environment.get_system_data_dirs()) { + search_paths += Path.build_filename(dir, "dino"); + } + } + public RootInterface load(string name, Dino.Application app) throws Error { if (Module.supported () == false) { throw new Error (-1, 0, "Plugins are not supported"); } - Module module = Module.open ("plugins/" + name, ModuleFlags.BIND_LAZY); + Module module = null; + string path = ""; + foreach (string prefix in search_paths) { + path = Path.build_filename(prefix, "plugins", name); + module = Module.open (path, ModuleFlags.BIND_LAZY); + if (module != null) break; + } if (module == null) { - throw new Error (-1, 1, Module.error ()); + throw new Error (-1, 1, Module.error ().replace(path, name)); } void* function; diff --git a/libdino/src/service/avatar_manager.vala b/libdino/src/service/avatar_manager.vala index 69e5580e..032bd576 100644 --- a/libdino/src/service/avatar_manager.vala +++ b/libdino/src/service/avatar_manager.vala @@ -20,7 +20,7 @@ public class AvatarManager : StreamInteractionModule, Object { private Database db; private HashMap user_avatars = new HashMap(Jid.hash_func, Jid.equals_func); private HashMap vcard_avatars = new HashMap(Jid.hash_func, Jid.equals_func); - private AvatarStorage avatar_storage = new AvatarStorage("./"); // TODO ihh + private AvatarStorage avatar_storage = new AvatarStorage(get_storage_dir()); private const int MAX_PIXEL = 192; public static void start(StreamInteractor stream_interactor, Database db) { @@ -28,10 +28,20 @@ public class AvatarManager : StreamInteractionModule, Object { stream_interactor.add_module(m); } + public static string get_storage_dir() { + return Path.build_filename(Application.get_storage_dir(), "avatars"); + } + private AvatarManager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); + stream_interactor.module_manager.initialize_account_modules.connect(initialize_avatar_modules); + } + + private void initialize_avatar_modules(Account account, ArrayList modules) { + modules.add(new Xep.UserAvatars.Module(avatar_storage)); + modules.add(new Xep.VCard.Module(avatar_storage)); } public Pixbuf? get_avatar(Account account, Jid jid) { diff --git a/libdino/src/service/avatar_storage.vala b/libdino/src/service/avatar_storage.vala index a9a8fb86..46b43d99 100644 --- a/libdino/src/service/avatar_storage.vala +++ b/libdino/src/service/avatar_storage.vala @@ -9,23 +9,28 @@ public class AvatarStorage : Xep.PixbufStorage, Object { public AvatarStorage(string folder) { this.folder = folder; + DirUtils.create_with_parents(folder, 0700); } public void store(string id, uint8[] data) { - File file = File.new_for_path(id); - if (file.query_exists()) file.delete(); //TODO y? - DataOutputStream fos = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION)); - fos.write(data); + File file = File.new_for_path(Path.build_filename(folder, id)); + try { + if (file.query_exists()) file.delete(); //TODO y? + DataOutputStream fos = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION)); + fos.write(data); + } catch (Error e) { + // Ignore: we failed in storing, so we refuse to display later... + } } public bool has_image(string id) { - File file = File.new_for_path(folder + id); + File file = File.new_for_path(Path.build_filename(folder, id)); return file.query_exists(); } public Pixbuf? get_image(string id) { try { - return new Pixbuf.from_file(folder + id); + return new Pixbuf.from_file(Path.build_filename(folder, id)); } catch (Error e) { return null; } diff --git a/libdino/src/service/message_manager.vala b/libdino/src/service/message_manager.vala index 11435262..b24aa12d 100644 --- a/libdino/src/service/message_manager.vala +++ b/libdino/src/service/message_manager.vala @@ -170,7 +170,7 @@ public class MessageManager : StreamInteractionModule, Object { 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); + Xmpp.Xep.DelayedDelivery.Module.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; diff --git a/libdino/src/service/module_manager.vala b/libdino/src/service/module_manager.vala index 896fb1e4..ab268876 100644 --- a/libdino/src/service/module_manager.vala +++ b/libdino/src/service/module_manager.vala @@ -8,7 +8,6 @@ namespace Dino { public class ModuleManager { private HashMap> module_map = new HashMap>(); - private AvatarStorage avatar_storage = new AvatarStorage("./"); private EntityCapabilitiesStorage entity_capabilities_storage; public signal void initialize_account_modules(Account account, ArrayList modules); @@ -65,8 +64,6 @@ public class ModuleManager { module_map[account].add(new Xep.Muc.Module()); module_map[account].add(new Xep.Pubsub.Module()); module_map[account].add(new Xep.EntityCapabilities.Module(entity_capabilities_storage)); - module_map[account].add(new Xep.UserAvatars.Module(avatar_storage)); - module_map[account].add(new Xep.VCard.Module(avatar_storage)); module_map[account].add(new Xep.MessageDeliveryReceipts.Module()); module_map[account].add(new Xep.ChatStateNotifications.Module()); module_map[account].add(new Xep.ChatMarkers.Module()); diff --git a/main/src/main.vala b/main/src/main.vala index dfaa661e..bec2d752 100644 --- a/main/src/main.vala +++ b/main/src/main.vala @@ -4,18 +4,22 @@ using Dino.Ui; namespace Dino { void main(string[] args) { - Gtk.init(ref args); - Dino.Ui.Application app = new Dino.Ui.Application(); - Plugins.Loader loader = new Plugins.Loader(); - foreach(string plugin in new string[]{"omemo", "openpgp"}) { - try { - loader.load(plugin, app); - } catch (Error e) { - print(@"Error loading plugin $plugin: $(e.message)\n"); + try{ + Plugins.Loader loader = new Plugins.Loader(args.length > 0 ? args[0] : null); + Gtk.init(ref args); + Dino.Ui.Application app = new Dino.Ui.Application(); + foreach (string plugin in new string[]{"omemo", "openpgp"}) { + try { + loader.load(plugin, app); + } catch (Error e) { + print(@"Error loading plugin $plugin: $(e.message)\n"); + } } + app.run(args); + loader.shutdown(); + } catch (Error e) { + print(@"Fatal error: $(e.message)\n"); } - app.run(args); - loader.shutdown(); } } \ No newline at end of file diff --git a/main/src/ui/application.vala b/main/src/ui/application.vala index 1adccf8e..fe930b82 100644 --- a/main/src/ui/application.vala +++ b/main/src/ui/application.vala @@ -11,10 +11,11 @@ public class Dino.Ui.Application : Dino.Application { private ConversationSummary.View? conversation_frame; private ChatInput? chat_input; - public Application() { + public Application() throws Error { Notify.init("dino"); notifications = new Notifications(stream_interaction); notifications.start(); + Environment.set_application_name("Dino"); load_css(); } diff --git a/plugins/omemo/src/plugin.vala b/plugins/omemo/src/plugin.vala index 04e02625..d3fee15d 100644 --- a/plugins/omemo/src/plugin.vala +++ b/plugins/omemo/src/plugin.vala @@ -9,10 +9,15 @@ public class Plugin : RootInterface, Object { public AccountSettingsEntry settings_entry; public void registered(Dino.Application app) { + // FIXME: Legacy import + if (FileUtils.test("omemo.db", FileTest.IS_REGULAR)) { + FileUtils.rename("omemo.db", Path.build_filename(Application.get_storage_dir(), "omemo.db")); + } + try { context = new Signal.Context(false); this.app = app; - this.db = new Database("omemo.db"); + this.db = new Database(Path.build_filename(Application.get_storage_dir(), "omemo.db")); this.list_entry = new EncryptionListEntry(this); this.settings_entry = new AccountSettingsEntry(this); this.app.plugin_registry.register_encryption_list_entry(list_entry); diff --git a/qlite/src/database.vala b/qlite/src/database.vala index 285e10a8..6d26b2b4 100644 --- a/qlite/src/database.vala +++ b/qlite/src/database.vala @@ -34,7 +34,6 @@ public class Database { } public void init(Table[] tables) throws DatabaseError { - print(@"Intializing database at $file_name\n"); Sqlite.config(Config.SERIALIZED); int ec = Sqlite.Database.open_v2(file_name, out db, OPEN_READWRITE | OPEN_CREATE | 0x00010000); if (ec != Sqlite.OK) { diff --git a/xmpp-vala/src/module/presence/module.vala b/xmpp-vala/src/module/presence/module.vala index 63545982..7e5dca17 100644 --- a/xmpp-vala/src/module/presence/module.vala +++ b/xmpp-vala/src/module/presence/module.vala @@ -56,9 +56,9 @@ namespace Xmpp.Presence { } public override void attach(XmppStream stream) { + stream.add_flag(new Flag()); stream.received_presence_stanza.connect(on_received_presence_stanza); stream.stream_negotiated.connect(on_stream_negotiated); - stream.add_flag(new Flag()); } public override void detach(XmppStream stream) { diff --git a/xmpp-vala/src/module/xep/0054_vcard/module.vala b/xmpp-vala/src/module/xep/0054_vcard/module.vala index 622f2657..b4c6910b 100644 --- a/xmpp-vala/src/module/xep/0054_vcard/module.vala +++ b/xmpp-vala/src/module/xep/0054_vcard/module.vala @@ -53,21 +53,18 @@ public class Module : XmppStreamModule { } else { iq.to = get_bare_jid(presence.from); } - stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, on_received_vcard); + stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, on_received_vcard, storage); } } - private static void on_received_vcard(XmppStream stream, Iq.Stanza iq) { + private static void on_received_vcard(XmppStream stream, Iq.Stanza iq, Object? storage_obj) { + PixbufStorage? storage = storage_obj as PixbufStorage; if (iq.is_error()) return; - StanzaNode? vcard_node = iq.stanza.get_subnode("vCard", NS_URI); - if (vcard_node == null) return; - StanzaNode? photo_node = vcard_node.get_subnode("PHOTO", NS_URI); - if (photo_node == null) return; - StanzaNode? binary_node = photo_node.get_subnode("BINVAL", NS_URI); - if (binary_node == null) return; - string? content = binary_node.get_string_content(); - if (content == null) return; - string sha1 = Checksum.compute_for_data(ChecksumType.SHA1, content.data); + string? res = iq.stanza.get_deep_string_content(@"$NS_URI:vCard", "PHOTO", "BINVAL"); + if (res == null) return; + uint8[] content = Base64.decode(res); + string sha1 = Checksum.compute_for_data(ChecksumType.SHA1, content); + storage.store(sha1, content); stream.get_module(IDENTITY).received_avatar(stream, iq.from, sha1); } }