Allow blocking any media

Stephen Paul Weber created

Permanently stores the hash so you never see that media again.

Change summary

src/cheogram/java/com/cheogram/android/BobTransfer.java                            |  2 
src/cheogram/res/values/strings.xml                                                |  1 
src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java              |  4 
src/main/java/eu/siacs/conversations/persistance/FileBackend.java                  | 22 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java           | 30 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java                  | 22 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java |  6 
src/main/res/menu/message_context.xml                                              |  4 
8 files changed, 82 insertions(+), 9 deletions(-)

Detailed changes

src/cheogram/java/com/cheogram/android/BobTransfer.java 🔗

@@ -111,7 +111,7 @@ public class BobTransfer implements Transferable {
 						outputStream.close();
 
 						finish(file);
-					} catch (IOException e) {
+					} catch (final IOException | XmppConnectionService.BlockedMediaException e) {
 						finish(null);
 						xmppConnectionService.showErrorToastInUi(R.string.download_failed_could_not_write_file);
 					}

src/cheogram/res/values/strings.xml 🔗

@@ -34,4 +34,5 @@
     <string name="moderate_message">Moderate</string>
     <string name="moderate_reason">Moderation Reason</string>
     <string name="unable_to_moderate">Unable to Moderate</string>
+    <string name="block_media">Block Media</string>
 </resources>

src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java 🔗

@@ -201,6 +201,10 @@ public class HttpDownloadConnection implements Transferable {
             Log.w(Config.LOGTAG, "Failed to rename downloaded file: " + e);
             file = tmp;
             message.setRelativeFilePath(file.getAbsolutePath());
+        } catch (final XmppConnectionService.BlockedMediaException e) {
+            file = tmp;
+            tmp.delete();
+            message.setDeleted(true);
         }
         message.setTransferable(null);
         mXmppConnectionService.updateMessage(message);

src/main/java/eu/siacs/conversations/persistance/FileBackend.java 🔗

@@ -756,8 +756,13 @@ public class FileBackend {
             extension = "oga";
         }
 
-        setupRelativeFilePath(message, uri, extension);
-        copyFileToPrivateStorage(mXmppConnectionService.getFileBackend().getFile(message), uri);
+        try {
+            setupRelativeFilePath(message, uri, extension);
+            copyFileToPrivateStorage(mXmppConnectionService.getFileBackend().getFile(message), uri);
+        } catch (final XmppConnectionService.BlockedMediaException e) {
+            message.setRelativeFilePath(null);
+            message.setDeleted(true);
+        }
     }
 
     private String getExtensionFromUri(final Uri uri) {
@@ -903,12 +908,17 @@ public class FileBackend {
             throw new FileCopyException(R.string.error_file_not_found);
         } catch (final IOException e) {
             throw new FileCopyException(R.string.error_io_exception);
+        } catch (final XmppConnectionService.BlockedMediaException e) {
+            tmp.delete();
+            message.setRelativeFilePath(null);
+            message.setDeleted(true);
+            return;
         }
         tmp.renameTo(getFile(message));
         updateFileParams(message, null, false);
     }
 
-    public void setupRelativeFilePath(final Message message, final Uri uri, final String extension) throws FileCopyException {
+    public void setupRelativeFilePath(final Message message, final Uri uri, final String extension) throws FileCopyException, XmppConnectionService.BlockedMediaException {
         try {
             setupRelativeFilePath(message, mXmppConnectionService.getContentResolver().openInputStream(uri), extension);
         } catch (final FileNotFoundException e) {
@@ -926,7 +936,7 @@ public class FileBackend {
         }
     }
 
-    public void setupRelativeFilePath(final Message message, final InputStream is, final String extension) throws IOException {
+    public void setupRelativeFilePath(final Message message, final InputStream is, final String extension) throws IOException, XmppConnectionService.BlockedMediaException {
         message.setRelativeFilePath(getStorageLocation(is, extension).getAbsolutePath());
     }
 
@@ -936,7 +946,7 @@ public class FileBackend {
         setupRelativeFilePath(message, filename, mime);
     }
 
-    public File getStorageLocation(final InputStream is, final String extension) throws IOException {
+    public File getStorageLocation(final InputStream is, final String extension) throws IOException, XmppConnectionService.BlockedMediaException {
         final String mime = MimeUtils.guessMimeTypeFromExtension(extension);
         Cid[] cids = calculateCids(is);
 
@@ -1796,7 +1806,7 @@ public class FileBackend {
                 for (int i = 0; i < cids.length; i++) {
                     mXmppConnectionService.saveCid(cids[i], file);
                 }
-            } catch (final IOException e) { }
+            } catch (final IOException | XmppConnectionService.BlockedMediaException e) { }
         }
     }
 

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -64,6 +64,8 @@ import org.openintents.openpgp.util.OpenPgpApi;
 import org.openintents.openpgp.util.OpenPgpServiceConnection;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.security.Security;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
@@ -559,10 +561,22 @@ public class XmppConnectionService extends Service {
         return this.databaseBackend.getFileForCid(cid);
     }
 
-    public void saveCid(Cid cid, File file) {
+    public void saveCid(Cid cid, File file) throws BlockedMediaException {
+        if (this.databaseBackend.isBlockedMedia(cid)) {
+            throw new BlockedMediaException();
+        }
         this.databaseBackend.saveCid(cid, file);
     }
 
+    public void blockMedia(File f) {
+        try {
+            Cid[] cids = getFileBackend().calculateCids(new FileInputStream(f));
+            for (Cid cid : cids) {
+                blockMedia(cid);
+            }
+        } catch (final IOException e) { }
+    }
+
     public void blockMedia(Cid cid) {
         this.databaseBackend.blockMedia(cid);
     }
@@ -5027,10 +5041,22 @@ public class XmppConnectionService extends Service {
         sendIqPacket(account, set, null);
     }
 
+    public void evictPreview(File f) {
+        if (mBitmapCache.remove(f.getAbsolutePath()) != null) {
+            Log.d(Config.LOGTAG, "deleted cached preview");
+        }
+        if (mDrawableCache.remove(f.getAbsolutePath()) != null) {
+            Log.d(Config.LOGTAG, "deleted cached preview");
+        }
+    }
+
     public void evictPreview(String uuid) {
         if (mBitmapCache.remove(uuid) != null) {
             Log.d(Config.LOGTAG, "deleted cached preview");
         }
+        if (mDrawableCache.remove(uuid) != null) {
+            Log.d(Config.LOGTAG, "deleted cached preview");
+        }
     }
 
     public interface OnMamPreferencesFetched {
@@ -5154,4 +5180,6 @@ public class XmppConnectionService extends Service {
             return Objects.hashCode(id, media, reconnecting);
         }
     }
+
+    public static class BlockedMediaException extends Exception { }
 }

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -1439,6 +1439,7 @@ public class ConversationFragment extends XmppFragment
             MenuItem saveAsSticker = menu.findItem(R.id.save_as_sticker);
             MenuItem downloadFile = menu.findItem(R.id.download_file);
             MenuItem cancelTransmission = menu.findItem(R.id.cancel_transmission);
+            MenuItem blockMedia = menu.findItem(R.id.block_media);
             MenuItem deleteFile = menu.findItem(R.id.delete_file);
             MenuItem showErrorMessage = menu.findItem(R.id.show_error_message);
             onlyThisThread.setVisible(!conversation.getLockThread() && m.getThread() != null);
@@ -1503,6 +1504,7 @@ public class ConversationFragment extends XmppFragment
                         || !path.startsWith("/")
                         || FileBackend.inConversationsDirectory(requireActivity(), path)) {
                     saveAsSticker.setVisible(true);
+                    blockMedia.setVisible(true);
                     deleteFile.setVisible(true);
                     deleteFile.setTitle(
                             activity.getString(
@@ -1577,6 +1579,23 @@ public class ConversationFragment extends XmppFragment
             case R.id.retry_decryption:
                 retryDecryption(selectedMessage);
                 return true;
+            case R.id.block_media:
+                new AlertDialog.Builder(activity)
+                    .setTitle(R.string.block_media)
+                    .setMessage("Do you really want to block this media in all messages?")
+                    .setPositiveButton(R.string.yes, (dialog, whichButton) -> {
+                        File f = activity.xmppConnectionService.getFileBackend().getFile(selectedMessage);
+                        activity.xmppConnectionService.blockMedia(f);
+                        if (activity.xmppConnectionService.getFileBackend().deleteFile(selectedMessage)) {
+                            selectedMessage.setDeleted(true);
+                            activity.xmppConnectionService.evictPreview(f);
+                            activity.xmppConnectionService.updateMessage(selectedMessage, false);
+                            activity.onConversationsListItemUpdated();
+                            refresh();
+                        }
+                    })
+                    .setNegativeButton(R.string.no, null).show();
+                return true;
             case R.id.delete_file:
                 deleteFile(selectedMessage);
                 return true;
@@ -2038,6 +2057,7 @@ public class ConversationFragment extends XmppFragment
         if (writeGranted(grantResults, permissions)) {
             if (activity != null && activity.xmppConnectionService != null) {
                 activity.xmppConnectionService.getBitmapCache().evictAll();
+                activity.xmppConnectionService.getDrawableCache().evictAll();
                 activity.xmppConnectionService.restartFileObserver();
             }
             refresh();
@@ -2385,7 +2405,7 @@ public class ConversationFragment extends XmppFragment
                 (dialog, which) -> {
                     if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) {
                         message.setDeleted(true);
-                        activity.xmppConnectionService.evictPreview(message.getUuid());
+                        activity.xmppConnectionService.evictPreview(activity.xmppConnectionService.getFileBackend().getFile(message));
                         activity.xmppConnectionService.updateMessage(message, false);
                         activity.onConversationsListItemUpdated();
                         refresh();

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java 🔗

@@ -38,6 +38,7 @@ import eu.siacs.conversations.entities.TransferablePlaceholder;
 import eu.siacs.conversations.parser.IqParser;
 import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.services.AbstractConnectionManager;
+import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.MimeUtils;
 import eu.siacs.conversations.xml.Element;
@@ -138,6 +139,11 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
                 } catch (final IOException e) {
                     finalFile = file;
                     message.setRelativeFilePath(finalFile.getAbsolutePath());
+                } catch (final XmppConnectionService.BlockedMediaException e) {
+                    finalFile = file;
+                    file.delete();
+                    message.setRelativeFilePath(null);
+                    message.setDeleted(true);
                 }
 
                 xmppConnectionService.getFileBackend().updateFileParams(message, null, false);

src/main/res/menu/message_context.xml 🔗

@@ -66,6 +66,10 @@
         android:id="@+id/cancel_transmission"
         android:title="@string/cancel_transmission"
         android:visible="false" />
+    <item
+        android:id="@+id/block_media"
+        android:title="Block Media"
+        android:visible="false" />
     <item
         android:id="@+id/delete_file"
         android:title="@string/delete_x_file"