diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index cc1c358de5288bc69117f81e1c5e67ffe3f84dd2..ef1952551f2a52c21b1e946c91f9bba0ec6dbeae 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -12,6 +12,8 @@ import java.util.List; import java.util.Locale; import java.util.Set; +import io.ipfs.cid.Cid; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.services.AvatarService; @@ -817,6 +819,14 @@ public class MucOptions { return avatar == null ? null : avatar.getFilename(); } + public Cid getAvatarCid() { + if (avatar != null) { + return avatar.cid(); + } + Avatar avatar = realJid != null ? getAccount().getRoster().getContact(realJid).getAvatar() : null; + return avatar == null ? null : avatar.cid(); + } + public Account getAccount() { return options.getAccount(); } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index ed7993a54fbee33bfc8f995004acce373ffd22f8..d00b64d12d51ce3959842fbc146607dd4d7553bf 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -274,6 +274,15 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL("PRAGMA cheogram.user_version = 5"); } + if(cheogramVersion < 6) { + db.execSQL( + "CREATE TABLE cheogram.blocked_media (" + + "cid TEXT NOT NULL PRIMARY KEY" + + ")" + ); + db.execSQL("PRAGMA cheogram.user_version = 6"); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); @@ -774,6 +783,24 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.insertWithOnConflict("cheogram.cids", null, cv, SQLiteDatabase.CONFLICT_REPLACE); } + public void blockMedia(Cid cid) { + SQLiteDatabase db = this.getWritableDatabase(); + ContentValues cv = new ContentValues(); + cv.put("cid", cid.toString()); + db.insertWithOnConflict("cheogram.blocked_media", null, cv, SQLiteDatabase.CONFLICT_REPLACE); + } + + public boolean isBlockedMedia(Cid cid) { + SQLiteDatabase db = this.getReadableDatabase(); + Cursor cursor = db.query("cheogram.blocked_media", new String[]{"count(*)"}, "cid=?", new String[]{cid.toString()}, null, null, null); + boolean is = false; + if (cursor.moveToNext()) { + is = cursor.getInt(0) > 0; + } + cursor.close(); + return is; + } + public void createConversation(Conversation conversation) { SQLiteDatabase db = this.getWritableDatabase(); db.insert(Conversation.TABLENAME, null, conversation.getContentValues()); diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index 2748fac69fdc991827a9d1f33fc19f7fdf9ba4a7..08d777e8f3218fdc662f483023c43ab2f339a2b3 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -1600,7 +1600,7 @@ public class FileBackend { return new File(mXmppConnectionService.getFilesDir(), "/avatars/"); } - private File getAvatarFile(String avatar) { + public File getAvatarFile(String avatar) { return new File(mXmppConnectionService.getCacheDir(), "/avatars/" + avatar); } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 5778a986a7419f47151d96ff97432cd42cfaeb43..ae3397565c6de04665b376bf512126fd8e4e9272 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -563,6 +563,10 @@ public class XmppConnectionService extends Service { this.databaseBackend.saveCid(cid, file); } + public void blockMedia(Cid cid) { + this.databaseBackend.blockMedia(cid); + } + public AvatarService getAvatarService() { return this.mAvatarService; } @@ -3889,6 +3893,11 @@ public class XmppConnectionService extends Service { } public void fetchAvatar(Account account, final Avatar avatar, final UiCallback callback) { + if (databaseBackend.isBlockedMedia(avatar.cid())) { + if (callback != null) callback.error(0, null); + return; + } + final String KEY = generateFetchKey(account, avatar); synchronized (this.mInProgressAvatarFetches) { if (mInProgressAvatarFetches.add(KEY)) { diff --git a/src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java b/src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java index da1ac7a4448043ec4910b9cf720dfe4f6bf8fdc7..9b2cc39b60bd08b444a68a724b84652c820d70e2 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java +++ b/src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java @@ -57,6 +57,7 @@ public final class MucDetailsContextMenuHelper { MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message); if (user != null && user.getRealJid() != null) { MenuItem showContactDetails = menu.findItem(R.id.action_contact_details); + MenuItem blockAvatar = menu.findItem(R.id.action_block_avatar); MenuItem startConversation = menu.findItem(R.id.start_conversation); MenuItem giveMembership = menu.findItem(R.id.give_membership); MenuItem removeMembership = menu.findItem(R.id.remove_membership); @@ -76,6 +77,9 @@ public final class MucDetailsContextMenuHelper { if ((contact != null && contact.showInRoster()) || mucOptions.isPrivateAndNonAnonymous()) { showContactDetails.setVisible(contact == null || !contact.isSelf()); } + if (user.getAvatar() != null) { + blockAvatar.setVisible(true); + } if ((activity instanceof ConferenceDetailsActivity || activity instanceof MucUsersActivity) && user.getRole() == MucOptions.Role.NONE) { invite.setVisible(true); } @@ -144,6 +148,14 @@ public final class MucDetailsContextMenuHelper { activity.switchToContactDetails(contact, fingerprint); } return true; + case R.id.action_block_avatar: + activity.xmppConnectionService.getFileBackend().getAvatarFile(user.getAvatar()).delete(); + activity.xmppConnectionService.blockMedia(user.getAvatarCid()); + activity.avatarService().clear(user); + if (user.getContact() != null) activity.avatarService().clear(user.getContact()); + user.setAvatar(null); + activity.xmppConnectionService.updateConversationUi(); + return true; case R.id.start_conversation: startConversation(user, activity); return true; @@ -226,4 +238,4 @@ public final class MucDetailsContextMenuHelper { activity.switchToConversation(newConversation); } } -} \ No newline at end of file +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java b/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java index a4397e466a93a1ad107976fa79cefcef9425f082..00bd671b81fbf867712eddc6023e0684a9fb05ec 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java +++ b/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java @@ -2,6 +2,11 @@ package eu.siacs.conversations.xmpp.pep; import android.util.Base64; +import java.security.NoSuchAlgorithmException; + +import io.ipfs.cid.Cid; + +import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.Jid; @@ -82,6 +87,16 @@ public class Avatar { } } + public Cid cid() { + if (sha1sum == null) return null; + + try { + return CryptoHelper.cid(CryptoHelper.hexToBytes(sha1sum), "sha-1"); + } catch (final NoSuchAlgorithmException e) { + return null; + } + } + public static Avatar parsePresence(Element x) { String hash = x == null ? null : x.findChildContent("photo"); if (hash == null) { diff --git a/src/main/res/menu/muc_details_context.xml b/src/main/res/menu/muc_details_context.xml index 54e0506eb73c6efd807dcc51bacf29866e0f4957..40abda96d757acd0f296c5e2f49ab061c9967ad1 100644 --- a/src/main/res/menu/muc_details_context.xml +++ b/src/main/res/menu/muc_details_context.xml @@ -8,6 +8,10 @@ android:id="@+id/action_contact_details" android:title="@string/action_contact_details" android:visible="false" /> +