Move storage into user directory and fix plugin search path
This commit is contained in:
parent
a9ea0e9f87
commit
e8f11178ec
|
@ -8,8 +8,17 @@ public class Dino.Application : Gtk.Application {
|
||||||
public StreamInteractor stream_interaction;
|
public StreamInteractor stream_interaction;
|
||||||
public Plugins.Registry plugin_registry = new Plugins.Registry();
|
public Plugins.Registry plugin_registry = new Plugins.Registry();
|
||||||
|
|
||||||
public Application() {
|
public Application() throws Error {
|
||||||
this.db = new Database("store.sqlite3");
|
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);
|
this.stream_interaction = new StreamInteractor(db);
|
||||||
|
|
||||||
AvatarManager.start(stream_interaction, db);
|
AvatarManager.start(stream_interaction, db);
|
||||||
|
@ -21,5 +30,9 @@ public class Dino.Application : Gtk.Application {
|
||||||
ConversationManager.start(stream_interaction, db);
|
ConversationManager.start(stream_interaction, db);
|
||||||
ChatInteraction.start(stream_interaction);
|
ChatInteraction.start(stream_interaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string get_storage_dir() {
|
||||||
|
return Path.build_filename(Environment.get_user_data_dir(), "dino");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,17 +14,40 @@ public class Loader : Object {
|
||||||
[CCode (has_target = false)]
|
[CCode (has_target = false)]
|
||||||
private delegate Type RegisterPluginFunction (Module module);
|
private delegate Type RegisterPluginFunction (Module module);
|
||||||
|
|
||||||
|
private string[] search_paths = new string[0];
|
||||||
private RootInterface[] plugins = new RootInterface[0];
|
private RootInterface[] plugins = new RootInterface[0];
|
||||||
private Info[] infos = new Info[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 {
|
public RootInterface load(string name, Dino.Application app) throws Error {
|
||||||
if (Module.supported () == false) {
|
if (Module.supported () == false) {
|
||||||
throw new Error (-1, 0, "Plugins are not supported");
|
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) {
|
if (module == null) {
|
||||||
throw new Error (-1, 1, Module.error ());
|
throw new Error (-1, 1, Module.error ().replace(path, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
void* function;
|
void* function;
|
||||||
|
|
|
@ -20,7 +20,7 @@ public class AvatarManager : StreamInteractionModule, Object {
|
||||||
private Database db;
|
private Database db;
|
||||||
private HashMap<Jid, string> user_avatars = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
|
private HashMap<Jid, string> user_avatars = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
|
||||||
private HashMap<Jid, string> vcard_avatars = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
|
private HashMap<Jid, string> vcard_avatars = new HashMap<Jid, string>(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;
|
private const int MAX_PIXEL = 192;
|
||||||
|
|
||||||
public static void start(StreamInteractor stream_interactor, Database db) {
|
public static void start(StreamInteractor stream_interactor, Database db) {
|
||||||
|
@ -28,10 +28,20 @@ public class AvatarManager : StreamInteractionModule, Object {
|
||||||
stream_interactor.add_module(m);
|
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) {
|
private AvatarManager(StreamInteractor stream_interactor, Database db) {
|
||||||
this.stream_interactor = stream_interactor;
|
this.stream_interactor = stream_interactor;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
stream_interactor.account_added.connect(on_account_added);
|
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<Core.XmppStreamModule> 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) {
|
public Pixbuf? get_avatar(Account account, Jid jid) {
|
||||||
|
|
|
@ -9,23 +9,28 @@ public class AvatarStorage : Xep.PixbufStorage, Object {
|
||||||
|
|
||||||
public AvatarStorage(string folder) {
|
public AvatarStorage(string folder) {
|
||||||
this.folder = folder;
|
this.folder = folder;
|
||||||
|
DirUtils.create_with_parents(folder, 0700);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void store(string id, uint8[] data) {
|
public void store(string id, uint8[] data) {
|
||||||
File file = File.new_for_path(id);
|
File file = File.new_for_path(Path.build_filename(folder, id));
|
||||||
|
try {
|
||||||
if (file.query_exists()) file.delete(); //TODO y?
|
if (file.query_exists()) file.delete(); //TODO y?
|
||||||
DataOutputStream fos = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION));
|
DataOutputStream fos = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION));
|
||||||
fos.write(data);
|
fos.write(data);
|
||||||
|
} catch (Error e) {
|
||||||
|
// Ignore: we failed in storing, so we refuse to display later...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool has_image(string id) {
|
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();
|
return file.query_exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pixbuf? get_image(string id) {
|
public Pixbuf? get_image(string id) {
|
||||||
try {
|
try {
|
||||||
return new Pixbuf.from_file(folder + id);
|
return new Pixbuf.from_file(Path.build_filename(folder, id));
|
||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,7 @@ public class MessageManager : StreamInteractionModule, Object {
|
||||||
pre_message_send(message, new_message, conversation);
|
pre_message_send(message, new_message, conversation);
|
||||||
if (message.marked == Entities.Message.Marked.UNSENT || message.marked == Entities.Message.Marked.WONTSEND) return;
|
if (message.marked == Entities.Message.Marked.UNSENT || message.marked == Entities.Message.Marked.WONTSEND) return;
|
||||||
if (delayed) {
|
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);
|
stream.get_module(Xmpp.Message.Module.IDENTITY).send_message(stream, new_message);
|
||||||
message.stanza_id = new_message.id;
|
message.stanza_id = new_message.id;
|
||||||
|
|
|
@ -8,7 +8,6 @@ namespace Dino {
|
||||||
public class ModuleManager {
|
public class ModuleManager {
|
||||||
private HashMap<Account, ArrayList<Core.XmppStreamModule>> module_map = new HashMap<Account, ArrayList<Core.XmppStreamModule>>();
|
private HashMap<Account, ArrayList<Core.XmppStreamModule>> module_map = new HashMap<Account, ArrayList<Core.XmppStreamModule>>();
|
||||||
|
|
||||||
private AvatarStorage avatar_storage = new AvatarStorage("./");
|
|
||||||
private EntityCapabilitiesStorage entity_capabilities_storage;
|
private EntityCapabilitiesStorage entity_capabilities_storage;
|
||||||
|
|
||||||
public signal void initialize_account_modules(Account account, ArrayList<Core.XmppStreamModule> modules);
|
public signal void initialize_account_modules(Account account, ArrayList<Core.XmppStreamModule> modules);
|
||||||
|
@ -65,8 +64,6 @@ public class ModuleManager {
|
||||||
module_map[account].add(new Xep.Muc.Module());
|
module_map[account].add(new Xep.Muc.Module());
|
||||||
module_map[account].add(new Xep.Pubsub.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.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.MessageDeliveryReceipts.Module());
|
||||||
module_map[account].add(new Xep.ChatStateNotifications.Module());
|
module_map[account].add(new Xep.ChatStateNotifications.Module());
|
||||||
module_map[account].add(new Xep.ChatMarkers.Module());
|
module_map[account].add(new Xep.ChatMarkers.Module());
|
||||||
|
|
|
@ -4,9 +4,10 @@ using Dino.Ui;
|
||||||
namespace Dino {
|
namespace Dino {
|
||||||
|
|
||||||
void main(string[] args) {
|
void main(string[] args) {
|
||||||
|
try{
|
||||||
|
Plugins.Loader loader = new Plugins.Loader(args.length > 0 ? args[0] : null);
|
||||||
Gtk.init(ref args);
|
Gtk.init(ref args);
|
||||||
Dino.Ui.Application app = new Dino.Ui.Application();
|
Dino.Ui.Application app = new Dino.Ui.Application();
|
||||||
Plugins.Loader loader = new Plugins.Loader();
|
|
||||||
foreach (string plugin in new string[]{"omemo", "openpgp"}) {
|
foreach (string plugin in new string[]{"omemo", "openpgp"}) {
|
||||||
try {
|
try {
|
||||||
loader.load(plugin, app);
|
loader.load(plugin, app);
|
||||||
|
@ -16,6 +17,9 @@ void main(string[] args) {
|
||||||
}
|
}
|
||||||
app.run(args);
|
app.run(args);
|
||||||
loader.shutdown();
|
loader.shutdown();
|
||||||
|
} catch (Error e) {
|
||||||
|
print(@"Fatal error: $(e.message)\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -11,10 +11,11 @@ public class Dino.Ui.Application : Dino.Application {
|
||||||
private ConversationSummary.View? conversation_frame;
|
private ConversationSummary.View? conversation_frame;
|
||||||
private ChatInput? chat_input;
|
private ChatInput? chat_input;
|
||||||
|
|
||||||
public Application() {
|
public Application() throws Error {
|
||||||
Notify.init("dino");
|
Notify.init("dino");
|
||||||
notifications = new Notifications(stream_interaction);
|
notifications = new Notifications(stream_interaction);
|
||||||
notifications.start();
|
notifications.start();
|
||||||
|
Environment.set_application_name("Dino");
|
||||||
|
|
||||||
load_css();
|
load_css();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,15 @@ public class Plugin : RootInterface, Object {
|
||||||
public AccountSettingsEntry settings_entry;
|
public AccountSettingsEntry settings_entry;
|
||||||
|
|
||||||
public void registered(Dino.Application app) {
|
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 {
|
try {
|
||||||
context = new Signal.Context(false);
|
context = new Signal.Context(false);
|
||||||
this.app = app;
|
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.list_entry = new EncryptionListEntry(this);
|
||||||
this.settings_entry = new AccountSettingsEntry(this);
|
this.settings_entry = new AccountSettingsEntry(this);
|
||||||
this.app.plugin_registry.register_encryption_list_entry(list_entry);
|
this.app.plugin_registry.register_encryption_list_entry(list_entry);
|
||||||
|
|
|
@ -34,7 +34,6 @@ public class Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(Table[] tables) throws DatabaseError {
|
public void init(Table[] tables) throws DatabaseError {
|
||||||
print(@"Intializing database at $file_name\n");
|
|
||||||
Sqlite.config(Config.SERIALIZED);
|
Sqlite.config(Config.SERIALIZED);
|
||||||
int ec = Sqlite.Database.open_v2(file_name, out db, OPEN_READWRITE | OPEN_CREATE | 0x00010000);
|
int ec = Sqlite.Database.open_v2(file_name, out db, OPEN_READWRITE | OPEN_CREATE | 0x00010000);
|
||||||
if (ec != Sqlite.OK) {
|
if (ec != Sqlite.OK) {
|
||||||
|
|
|
@ -56,9 +56,9 @@ namespace Xmpp.Presence {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void attach(XmppStream stream) {
|
public override void attach(XmppStream stream) {
|
||||||
|
stream.add_flag(new Flag());
|
||||||
stream.received_presence_stanza.connect(on_received_presence_stanza);
|
stream.received_presence_stanza.connect(on_received_presence_stanza);
|
||||||
stream.stream_negotiated.connect(on_stream_negotiated);
|
stream.stream_negotiated.connect(on_stream_negotiated);
|
||||||
stream.add_flag(new Flag());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void detach(XmppStream stream) {
|
public override void detach(XmppStream stream) {
|
||||||
|
|
|
@ -53,21 +53,18 @@ public class Module : XmppStreamModule {
|
||||||
} else {
|
} else {
|
||||||
iq.to = get_bare_jid(presence.from);
|
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;
|
if (iq.is_error()) return;
|
||||||
StanzaNode? vcard_node = iq.stanza.get_subnode("vCard", NS_URI);
|
string? res = iq.stanza.get_deep_string_content(@"$NS_URI:vCard", "PHOTO", "BINVAL");
|
||||||
if (vcard_node == null) return;
|
if (res == null) return;
|
||||||
StanzaNode? photo_node = vcard_node.get_subnode("PHOTO", NS_URI);
|
uint8[] content = Base64.decode(res);
|
||||||
if (photo_node == null) return;
|
string sha1 = Checksum.compute_for_data(ChecksumType.SHA1, content);
|
||||||
StanzaNode? binary_node = photo_node.get_subnode("BINVAL", NS_URI);
|
storage.store(sha1, content);
|
||||||
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);
|
|
||||||
stream.get_module(IDENTITY).received_avatar(stream, iq.from, sha1);
|
stream.get_module(IDENTITY).received_avatar(stream, iq.from, sha1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue