diff --git a/src/org/thoughtcrime/securesms/backup/FullBackupImporter.java b/src/org/thoughtcrime/securesms/backup/FullBackupImporter.java index 6f9ad51f87..7344934452 100644 --- a/src/org/thoughtcrime/securesms/backup/FullBackupImporter.java +++ b/src/org/thoughtcrime/securesms/backup/FullBackupImporter.java @@ -30,10 +30,12 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.Conversions; +import org.thoughtcrime.securesms.util.SqlUtil; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.kdf.HKDFv3; import org.whispersystems.libsignal.util.ByteUtil; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -82,7 +84,7 @@ public class FullBackupImporter extends FullBackupBase { else if (frame.hasPreference()) processPreference(context, frame.getPreference()); else if (frame.hasAttachment()) processAttachment(context, attachmentSecret, db, frame.getAttachment(), inputStream); else if (frame.hasSticker()) processSticker(context, attachmentSecret, db, frame.getSticker(), inputStream); - else if (frame.hasAvatar()) processAvatar(context, frame.getAvatar(), inputStream); + else if (frame.hasAvatar()) processAvatar(context, db, frame.getAvatar(), inputStream); } db.setTransactionSuccessful(); @@ -172,11 +174,23 @@ public class FullBackupImporter extends FullBackupBase { new String[] {String.valueOf(sticker.getRowId())}); } - private static void processAvatar(@NonNull Context context, @NonNull BackupProtos.Avatar avatar, @NonNull BackupRecordInputStream inputStream) throws IOException { - Recipient recipient = avatar.hasRecipientId() ? Recipient.resolved(RecipientId.from(avatar.getRecipientId())) - : Recipient.external(context, avatar.getName()); + private static void processAvatar(@NonNull Context context, @NonNull SQLiteDatabase db, @NonNull BackupProtos.Avatar avatar, @NonNull BackupRecordInputStream inputStream) throws IOException { + if (avatar.hasRecipientId()) { + RecipientId recipientId = RecipientId.from(avatar.getRecipientId()); + inputStream.readAttachmentTo(new FileOutputStream(AvatarHelper.getAvatarFile(context, recipientId)), avatar.getLength()); + } else { + if (avatar.hasName() && SqlUtil.tableExists(db, "recipient_preferences")) { + Log.w(TAG, "Avatar is missing a recipientId. Clearing signal_profile_avatar (legacy) so it can be fetched later."); + db.execSQL("UPDATE recipient_preferences SET signal_profile_avatar = NULL WHERE recipient_ids = ?", new String[] { avatar.getName() }); + } else if (avatar.hasName() && SqlUtil.tableExists(db, "recipient")) { + Log.w(TAG, "Avatar is missing a recipientId. Clearing signal_profile_avatar so it can be fetched later."); + db.execSQL("UPDATE recipient SET signal_profile_avatar = NULL WHERE phone = ?", new String[] { avatar.getName() }); + } else { + Log.w(TAG, "Avatar is missing a recipientId. Skipping avatar restore."); + } - inputStream.readAttachmentTo(new FileOutputStream(AvatarHelper.getAvatarFile(context, recipient.getId())), avatar.getLength()); + inputStream.readAttachmentTo(new ByteArrayOutputStream(), avatar.getLength()); + } } @SuppressLint("ApplySharedPref") diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 7aaf096105..2fa9982871 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.SqlUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import java.io.File; @@ -296,12 +297,12 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { } // Note: This column only being checked due to upgrade issues as described in #8184 - if (oldVersion < QUOTE_MISSING && !columnExists(db, "mms", "quote_missing")) { + if (oldVersion < QUOTE_MISSING && !SqlUtil.columnExists(db, "mms", "quote_missing")) { db.execSQL("ALTER TABLE mms ADD COLUMN quote_missing INTEGER DEFAULT 0"); } // Note: The column only being checked due to upgrade issues as described in #8184 - if (oldVersion < NOTIFICATION_CHANNELS && !columnExists(db, "recipient_preferences", "notification_channel")) { + if (oldVersion < NOTIFICATION_CHANNELS && !SqlUtil.columnExists(db, "recipient_preferences", "notification_channel")) { db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN notification_channel TEXT DEFAULT NULL"); NotificationChannels.create(context); @@ -354,7 +355,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { // 4.30.8 included a migration, but not a correct CREATE_TABLE statement, so we need to add // this column if it isn't present. if (oldVersion < ATTACHMENT_CAPTIONS_FIX) { - if (!columnExists(db, "part", "caption")) { + if (!SqlUtil.columnExists(db, "part", "caption")) { db.execSQL("ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL"); } } @@ -637,20 +638,4 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { for (String statement : statements) db.execSQL(statement); } - - private static boolean columnExists(@NonNull SQLiteDatabase db, @NonNull String table, @NonNull String column) { - try (Cursor cursor = db.rawQuery("PRAGMA table_info(" + table + ")", null)) { - int nameColumnIndex = cursor.getColumnIndexOrThrow("name"); - - while (cursor.moveToNext()) { - String name = cursor.getString(nameColumnIndex); - - if (name.equals(column)) { - return true; - } - } - } - - return false; - } } diff --git a/src/org/thoughtcrime/securesms/util/SqlUtil.java b/src/org/thoughtcrime/securesms/util/SqlUtil.java new file mode 100644 index 0000000000..e3019b341c --- /dev/null +++ b/src/org/thoughtcrime/securesms/util/SqlUtil.java @@ -0,0 +1,34 @@ +package org.thoughtcrime.securesms.util; + +import android.database.Cursor; + +import androidx.annotation.NonNull; + +import net.sqlcipher.database.SQLiteDatabase; + +public final class SqlUtil { + private SqlUtil() {} + + + public static boolean tableExists(@NonNull SQLiteDatabase db, @NonNull String table) { + try (Cursor cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type=? AND name=?", new String[] { "table", table })) { + return cursor != null && cursor.moveToNext(); + } + } + + public static boolean columnExists(@NonNull SQLiteDatabase db, @NonNull String table, @NonNull String column) { + try (Cursor cursor = db.rawQuery("PRAGMA table_info(" + table + ")", null)) { + int nameColumnIndex = cursor.getColumnIndexOrThrow("name"); + + while (cursor.moveToNext()) { + String name = cursor.getString(nameColumnIndex); + + if (name.equals(column)) { + return true; + } + } + } + + return false; + } +}