aboutsummaryrefslogtreecommitdiff
path: root/libdino
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2020-06-10 19:53:56 +0200
committerfiaxh <git@lightrise.org>2020-06-10 19:53:56 +0200
commit50c55c7f55aff6622d242bdcf2b58d5f7956f28e (patch)
treeb8c26336bb3003defafe987980c17fd2fdc07bd4 /libdino
parentdaf803e7730555639517de18c89da42e73557679 (diff)
downloaddino-50c55c7f55aff6622d242bdcf2b58d5f7956f28e.tar.gz
dino-50c55c7f55aff6622d242bdcf2b58d5f7956f28e.zip
Fetch avatars only when they are used
Diffstat (limited to 'libdino')
-rw-r--r--libdino/CMakeLists.txt1
-rw-r--r--libdino/src/service/avatar_manager.vala169
-rw-r--r--libdino/src/service/avatar_storage.vala54
-rw-r--r--libdino/src/service/muc_manager.vala15
4 files changed, 131 insertions, 108 deletions
diff --git a/libdino/CMakeLists.txt b/libdino/CMakeLists.txt
index 1e16cdff..95b95ae2 100644
--- a/libdino/CMakeLists.txt
+++ b/libdino/CMakeLists.txt
@@ -25,7 +25,6 @@ SOURCES
src/plugin/registry.vala
src/service/avatar_manager.vala
- src/service/avatar_storage.vala
src/service/blocking_manager.vala
src/service/chat_interaction.vala
src/service/connection_manager.vala
diff --git a/libdino/src/service/avatar_manager.vala b/libdino/src/service/avatar_manager.vala
index 5bb6b8e3..ba6efd31 100644
--- a/libdino/src/service/avatar_manager.vala
+++ b/libdino/src/service/avatar_manager.vala
@@ -11,7 +11,7 @@ public class AvatarManager : StreamInteractionModule, Object {
public static ModuleIdentity<AvatarManager> IDENTITY = new ModuleIdentity<AvatarManager>("avatar_manager");
public string id { get { return IDENTITY.id; } }
- public signal void received_avatar(Pixbuf avatar, Jid jid, Account account);
+ public signal void received_avatar(Jid jid, Account account);
private enum Source {
USER_AVATARS,
@@ -20,9 +20,9 @@ public class AvatarManager : StreamInteractionModule, Object {
private StreamInteractor stream_interactor;
private Database db;
+ private string folder = null;
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 AvatarStorage avatar_storage = new AvatarStorage(get_storage_dir());
private HashMap<string, Pixbuf> cached_pixbuf = new HashMap<string, Pixbuf>();
private HashMap<string, Gee.List<SourceFuncWrapper>> pending_pixbuf = new HashMap<string, Gee.List<SourceFuncWrapper>>();
private const int MAX_PIXEL = 192;
@@ -32,20 +32,17 @@ public class AvatarManager : StreamInteractionModule, Object {
stream_interactor.add_module(m);
}
- public static string get_storage_dir() {
- return Path.build_filename(Dino.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);
- }
+ this.folder = Path.build_filename(Dino.get_storage_dir(), "avatars");
+ DirUtils.create_with_parents(this.folder, 0700);
- private void initialize_avatar_modules(Account account, ArrayList<XmppStreamModule> modules) {
- modules.add(new Xep.UserAvatars.Module(avatar_storage));
- modules.add(new Xep.VCard.Module(avatar_storage));
+ stream_interactor.account_added.connect(on_account_added);
+ stream_interactor.module_manager.initialize_account_modules.connect((_, modules) => {
+ modules.add(new Xep.UserAvatars.Module());
+ modules.add(new Xep.VCard.Module());
+ });
}
private async Pixbuf? get_avatar_by_hash(string hash) {
@@ -58,7 +55,7 @@ public class AvatarManager : StreamInteractionModule, Object {
return cached_pixbuf[hash];
}
pending_pixbuf[hash] = new ArrayList<SourceFuncWrapper>();
- Pixbuf? image = yield avatar_storage.get_image(hash);
+ Pixbuf? image = yield get_image(hash);
if (image != null) {
cached_pixbuf[hash] = image;
} else {
@@ -70,40 +67,61 @@ public class AvatarManager : StreamInteractionModule, Object {
return image;
}
- public bool has_avatar(Account account, Jid jid) {
- string? hash = get_avatar_hash(account, jid);
- if (hash != null) {
- if (cached_pixbuf.has_key(hash)) {
- return true;
- }
- return avatar_storage.has_image(hash);
+ public async Pixbuf? get_avatar(Account account, Jid jid_) {
+ Jid jid = jid_;
+ if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid_, account)) {
+ jid = jid_.bare_jid;
}
- return false;
- }
- public async Pixbuf? get_avatar(Account account, Jid jid) {
- Jid jid_ = jid;
- if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid, account)) {
- jid_ = jid.bare_jid;
+ int source = -1;
+ string? hash = null;
+ if (user_avatars.has_key(jid)) {
+ hash = user_avatars[jid];
+ source = 1;
+ } else if (vcard_avatars.has_key(jid)) {
+ hash = vcard_avatars[jid];
+ source = 2;
}
- string? hash = get_avatar_hash(account, jid_);
- if (hash != null) {
- return yield get_avatar_by_hash(hash);
+ if (hash == null) return null;
+
+ if (cached_pixbuf.has_key(hash)) {
+ return cached_pixbuf[hash];
+ }
+
+ XmppStream? stream = stream_interactor.get_stream(account);
+ if (stream == null || !stream.negotiation_complete) return null;
+
+ if (pending_pixbuf.has_key(hash)) {
+ pending_pixbuf[hash].add(new SourceFuncWrapper(get_avatar.callback));
+ yield;
+ return cached_pixbuf[hash];
}
- return null;
- }
- private string? get_avatar_hash(Account account, Jid jid) {
- string? user_avatars_id = user_avatars[jid];
- if (user_avatars_id != null) {
- return user_avatars_id;
+ pending_pixbuf[hash] = new ArrayList<SourceFuncWrapper>();
+ Pixbuf? image = yield get_image(hash);
+ if (image != null) {
+ cached_pixbuf[hash] = image;
+ } else {
+ Bytes? bytes = null;
+ if (source == 1) {
+ bytes = yield Xmpp.Xep.UserAvatars.fetch_image(stream, jid, hash);
+ } else if (source == 2) {
+ bytes = yield Xmpp.Xep.VCard.fetch_image(stream, jid, hash);
+ if (bytes == null && jid.is_bare()) {
+ db.avatar.delete().with(db.avatar.jid_id, "=", db.get_jid_id(jid)).perform();
+ }
+ }
+ if (bytes != null) {
+ store_image(hash, bytes);
+ image = yield get_image(hash);
+ }
+ cached_pixbuf[hash] = image;
}
- string? vcard_avatars_id = vcard_avatars[jid];
- if (vcard_avatars_id != null) {
- return vcard_avatars_id;
+ foreach (SourceFuncWrapper sfw in pending_pixbuf[hash]) {
+ sfw.sfun();
}
- return null;
+ return image;
}
public void publish(Account account, string file) {
@@ -120,7 +138,7 @@ public class AvatarManager : StreamInteractionModule, Object {
pixbuf.save_to_buffer(out buffer, "png");
XmppStream stream = stream_interactor.get_stream(account);
if (stream != null) {
- stream.get_module(Xep.UserAvatars.Module.IDENTITY).publish_png(stream, buffer, pixbuf.width, pixbuf.height);
+ Xmpp.Xep.UserAvatars.publish_png(stream, buffer, pixbuf.width, pixbuf.height);
}
} catch (Error e) {
warning(e.message);
@@ -128,10 +146,10 @@ public class AvatarManager : StreamInteractionModule, Object {
}
private void on_account_added(Account account) {
- stream_interactor.module_manager.get_module(account, Xep.UserAvatars.Module.IDENTITY).received_avatar.connect((stream, jid, id) =>
+ stream_interactor.module_manager.get_module(account, Xep.UserAvatars.Module.IDENTITY).received_avatar_hash.connect((stream, jid, id) =>
on_user_avatar_received.begin(account, jid, id)
);
- stream_interactor.module_manager.get_module(account, Xep.VCard.Module.IDENTITY).received_avatar.connect((stream, jid, id) =>
+ stream_interactor.module_manager.get_module(account, Xep.VCard.Module.IDENTITY).received_avatar_hash.connect((stream, jid, id) =>
on_vcard_avatar_received.begin(account, jid, id)
);
@@ -139,32 +157,38 @@ public class AvatarManager : StreamInteractionModule, Object {
user_avatars[entry.key] = entry.value;
}
foreach (var entry in get_avatar_hashes(account, Source.VCARD).entries) {
+
+ // FIXME: remove. temporary to remove falsely saved avatars.
+ if (stream_interactor.get_module(MucManager.IDENTITY).is_groupchat(entry.key, account)) {
+ db.avatar.delete().with(db.avatar.jid_id, "=", db.get_jid_id(entry.key)).perform();
+ continue;
+ }
+
vcard_avatars[entry.key] = entry.value;
}
}
- private async void on_user_avatar_received(Account account, Jid jid, string id) {
+ private async void on_user_avatar_received(Account account, Jid jid_, string id) {
+ Jid jid = jid_.bare_jid;
+
if (!user_avatars.has_key(jid) || user_avatars[jid] != id) {
user_avatars[jid] = id;
set_avatar_hash(account, jid, id, Source.USER_AVATARS);
}
- Pixbuf? avatar = yield get_avatar_by_hash(id);
- if (avatar != null) {
- received_avatar(avatar, jid, account);
- }
+ received_avatar(jid, account);
}
- private async void on_vcard_avatar_received(Account account, Jid jid, string id) {
+ private async void on_vcard_avatar_received(Account account, Jid jid_, string id) {
+ bool is_gc = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(jid_.bare_jid, account);
+ Jid jid = is_gc ? jid_ : jid_.bare_jid;
+
if (!vcard_avatars.has_key(jid) || vcard_avatars[jid] != id) {
vcard_avatars[jid] = id;
- if (!jid.is_full()) { // don't save MUC occupant avatars
+ if (jid.is_bare()) { // don't save MUC occupant avatars
set_avatar_hash(account, jid, id, Source.VCARD);
}
}
- Pixbuf? avatar = yield get_avatar_by_hash(id);
- if (avatar != null) {
- received_avatar(avatar, jid, account);
- }
+ received_avatar(jid, account);
}
public void set_avatar_hash(Account account, Jid jid, string hash, int type) {
@@ -185,6 +209,45 @@ public class AvatarManager : StreamInteractionModule, Object {
}
return ret;
}
+
+ public void store_image(string id, Bytes 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_bytes_async.begin(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(Path.build_filename(folder, id));
+ return file.query_exists();
+ }
+
+ public async Pixbuf? get_image(string id) {
+ try {
+ File file = File.new_for_path(Path.build_filename(folder, id));
+ FileInputStream stream = yield file.read_async();
+
+ uint8 fbuf[1024];
+ size_t size;
+
+ Checksum checksum = new Checksum (ChecksumType.SHA1);
+ while ((size = yield stream.read_async(fbuf)) > 0) {
+ checksum.update(fbuf, size);
+ }
+
+ if (checksum.get_string() != id) {
+ FileUtils.remove(file.get_path());
+ }
+ stream.seek(0, SeekType.SET);
+ return yield new Pixbuf.from_stream_async(stream, null);
+ } catch (Error e) {
+ return null;
+ }
+ }
}
}
diff --git a/libdino/src/service/avatar_storage.vala b/libdino/src/service/avatar_storage.vala
deleted file mode 100644
index 26c98e12..00000000
--- a/libdino/src/service/avatar_storage.vala
+++ /dev/null
@@ -1,54 +0,0 @@
-using Gdk;
-
-using Xmpp;
-
-namespace Dino {
-public class AvatarStorage : Xep.PixbufStorage, Object {
-
- string folder;
-
- public AvatarStorage(string folder) {
- this.folder = folder;
- DirUtils.create_with_parents(folder, 0700);
- }
-
- public void store(string id, Bytes 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_bytes_async.begin(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(Path.build_filename(folder, id));
- return file.query_exists();
- }
-
- public async Pixbuf? get_image(string id) {
- try {
- File file = File.new_for_path(Path.build_filename(folder, id));
- FileInputStream stream = yield file.read_async();
-
- uint8 fbuf[1024];
- size_t size;
-
- Checksum checksum = new Checksum (ChecksumType.SHA1);
- while ((size = yield stream.read_async(fbuf)) > 0) {
- checksum.update(fbuf, size);
- }
-
- if (checksum.get_string() != id) {
- FileUtils.remove(file.get_path());
- }
- stream.seek(0, SeekType.SET);
- return yield new Pixbuf.from_stream_async(stream, null);
- } catch (Error e) {
- return null;
- }
- }
-}
-}
diff --git a/libdino/src/service/muc_manager.vala b/libdino/src/service/muc_manager.vala
index 3b6d9b5f..95fb2f8b 100644
--- a/libdino/src/service/muc_manager.vala
+++ b/libdino/src/service/muc_manager.vala
@@ -21,9 +21,11 @@ public class MucManager : StreamInteractionModule, Object {
public signal void conference_removed(Account account, Jid jid);
private StreamInteractor stream_interactor;
+ private HashMap<Account, Gee.List<Jid>> mucs_joining = new HashMap<Account, ArrayList<Jid>>(Account.hash_func, Account.equals_func);
private HashMap<Jid, Xep.Muc.MucEnterError> enter_errors = new HashMap<Jid, Xep.Muc.MucEnterError>(Jid.hash_func, Jid.equals_func);
private ReceivedMessageListener received_message_listener;
private HashMap<Account, BookmarksProvider> bookmarks_provider = new HashMap<Account, BookmarksProvider>(Account.hash_func, Account.equals_func);
+
public static void start(StreamInteractor stream_interactor) {
MucManager m = new MucManager(stream_interactor);
stream_interactor.add_module(m);
@@ -45,6 +47,7 @@ public class MucManager : StreamInteractionModule, Object {
public async Muc.JoinResult? join(Account account, Jid jid, string? nick, string? password) {
XmppStream? stream = stream_interactor.get_stream(account);
if (stream == null) return null;
+
string nick_ = (nick ?? account.localpart) ?? account.domainpart;
DateTime? history_since = null;
@@ -54,8 +57,15 @@ public class MucManager : StreamInteractionModule, Object {
if (last_message != null) history_since = last_message.time;
}
+ if (!mucs_joining.has_key(account)) {
+ mucs_joining[account] = new ArrayList<Jid>();
+ }
+ mucs_joining[account].add(jid);
+
Muc.JoinResult? res = yield stream.get_module(Xep.Muc.Module.IDENTITY).enter(stream, jid.bare_jid, nick_, password, history_since);
+ mucs_joining[account].remove(jid);
+
if (res.nick != null) {
// Join completed
enter_errors.unset(jid);
@@ -213,6 +223,11 @@ public class MucManager : StreamInteractionModule, Object {
return !jid.is_full() && conversation != null;
}
+ public bool might_be_groupchat(Jid jid, Account account) {
+ if (mucs_joining.has_key(account) && mucs_joining[account].contains(jid)) return true;
+ return is_groupchat(jid, account);
+ }
+
public bool is_groupchat_occupant(Jid jid, Account account) {
return is_groupchat(jid.bare_jid, account) && jid.resourcepart != null;
}