From af6f16bdb6f17a8f9c27b7134ceabb70d3d6c800 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Sat, 11 Feb 2023 12:48:42 -0500 Subject: [PATCH] Move Backups.proto to Wire. --- .../backup/BackupRecordInputStream.java | 18 +- .../securesms/backup/BackupVerifier.kt | 25 +-- .../securesms/backup/FullBackupExporter.java | 123 +++++++------- .../securesms/backup/FullBackupImporter.java | 154 ++++++++++-------- .../securesms/util/TextSecurePreferences.java | 43 +++-- .../main/{proto => protowire}/Backups.proto | 3 +- 6 files changed, 197 insertions(+), 169 deletions(-) rename app/src/main/{proto => protowire}/Backups.proto (94%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupRecordInputStream.java b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupRecordInputStream.java index 9c11e14e1..e90f3169b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupRecordInputStream.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupRecordInputStream.java @@ -6,6 +6,8 @@ import org.signal.core.util.Conversions; import org.signal.core.util.StreamUtil; import org.signal.libsignal.protocol.kdf.HKDF; import org.signal.libsignal.protocol.util.ByteUtil; +import org.thoughtcrime.securesms.backup.proto.BackupFrame; +import org.thoughtcrime.securesms.backup.proto.Header; import java.io.IOException; import java.io.InputStream; @@ -45,21 +47,21 @@ class BackupRecordInputStream extends FullBackupBase.BackupStream { byte[] headerFrame = new byte[headerLength]; StreamUtil.readFully(in, headerFrame); - BackupProtos.BackupFrame frame = BackupProtos.BackupFrame.parseFrom(headerFrame); + BackupFrame frame = BackupFrame.ADAPTER.decode(headerFrame); - if (!frame.hasHeader()) { + if (frame.header_ == null) { throw new IOException("Backup stream does not start with header!"); } - BackupProtos.Header header = frame.getHeader(); + Header header = frame.header_; - this.iv = header.getIv().toByteArray(); + this.iv = header.iv.toByteArray(); if (iv.length != 16) { throw new IOException("Invalid IV length!"); } - byte[] key = getBackupKey(passphrase, header.hasSalt() ? header.getSalt().toByteArray() : null); + byte[] key = getBackupKey(passphrase, header.salt != null ? header.salt.toByteArray() : null); byte[] derived = HKDF.deriveSecrets(key, "Backup Export".getBytes(), 64); byte[][] split = ByteUtil.split(derived, 32, 32); @@ -76,7 +78,7 @@ class BackupRecordInputStream extends FullBackupBase.BackupStream { } } - BackupProtos.BackupFrame readFrame() throws IOException { + BackupFrame readFrame() throws IOException { return readFrame(in); } @@ -128,7 +130,7 @@ class BackupRecordInputStream extends FullBackupBase.BackupStream { } } - private BackupProtos.BackupFrame readFrame(InputStream in) throws IOException { + private BackupFrame readFrame(InputStream in) throws IOException { try { byte[] length = new byte[4]; StreamUtil.readFully(in, length); @@ -151,7 +153,7 @@ class BackupRecordInputStream extends FullBackupBase.BackupStream { byte[] plaintext = cipher.doFinal(frame, 0, frame.length - 10); - return BackupProtos.BackupFrame.parseFrom(plaintext); + return BackupFrame.ADAPTER.decode(plaintext); } catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { throw new AssertionError(e); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupVerifier.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupVerifier.kt index 0bcc2d59c..73073ac01 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupVerifier.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupVerifier.kt @@ -2,7 +2,10 @@ package org.thoughtcrime.securesms.backup import org.greenrobot.eventbus.EventBus import org.signal.core.util.logging.Log -import org.thoughtcrime.securesms.backup.BackupProtos.BackupFrame +import org.thoughtcrime.securesms.backup.proto.Attachment +import org.thoughtcrime.securesms.backup.proto.Avatar +import org.thoughtcrime.securesms.backup.proto.BackupFrame +import org.thoughtcrime.securesms.backup.proto.Sticker import java.io.IOException import java.io.InputStream import java.io.OutputStream @@ -23,11 +26,11 @@ object BackupVerifier { var frame: BackupFrame = inputStream.readFrame() cipherStream.use { - while (!frame.end && !cancellationSignal.isCanceled) { + while (frame.end != true && !cancellationSignal.isCanceled) { val verified = when { - frame.hasAttachment() -> verifyAttachment(frame.attachment, inputStream) - frame.hasSticker() -> verifySticker(frame.sticker, inputStream) - frame.hasAvatar() -> verifyAvatar(frame.avatar, inputStream) + frame.attachment != null -> verifyAttachment(frame.attachment!!, inputStream) + frame.sticker != null -> verifySticker(frame.sticker!!, inputStream) + frame.avatar != null -> verifyAvatar(frame.avatar!!, inputStream) else -> true } @@ -48,9 +51,9 @@ object BackupVerifier { return true } - private fun verifyAttachment(attachment: BackupProtos.Attachment, inputStream: BackupRecordInputStream): Boolean { + private fun verifyAttachment(attachment: Attachment, inputStream: BackupRecordInputStream): Boolean { try { - inputStream.readAttachmentTo(NullOutputStream, attachment.length) + inputStream.readAttachmentTo(NullOutputStream, attachment.length ?: 0) } catch (e: IOException) { Log.w(TAG, "Bad attachment id: ${attachment.attachmentId} len: ${attachment.length}", e) return false @@ -59,9 +62,9 @@ object BackupVerifier { return true } - private fun verifySticker(sticker: BackupProtos.Sticker, inputStream: BackupRecordInputStream): Boolean { + private fun verifySticker(sticker: Sticker, inputStream: BackupRecordInputStream): Boolean { try { - inputStream.readAttachmentTo(NullOutputStream, sticker.length) + inputStream.readAttachmentTo(NullOutputStream, sticker.length ?: 0) } catch (e: IOException) { Log.w(TAG, "Bad sticker id: ${sticker.rowId} len: ${sticker.length}", e) return false @@ -69,9 +72,9 @@ object BackupVerifier { return true } - private fun verifyAvatar(avatar: BackupProtos.Avatar, inputStream: BackupRecordInputStream): Boolean { + private fun verifyAvatar(avatar: Avatar, inputStream: BackupRecordInputStream): Boolean { try { - inputStream.readAttachmentTo(NullOutputStream, avatar.length) + inputStream.readAttachmentTo(NullOutputStream, avatar.length ?: 0) } catch (e: IOException) { Log.w(TAG, "Bad avatar id: ${avatar.recipientId} len: ${avatar.length}", e) return false diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java index bdac12947..fec8419ff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java @@ -12,7 +12,6 @@ import androidx.annotation.VisibleForTesting; import androidx.documentfile.provider.DocumentFile; import com.annimon.stream.function.Predicate; -import com.google.protobuf.ByteString; import net.zetetic.database.sqlcipher.SQLiteDatabase; @@ -26,6 +25,15 @@ import org.signal.core.util.logging.Log; import org.signal.libsignal.protocol.kdf.HKDF; import org.signal.libsignal.protocol.util.ByteUtil; import org.thoughtcrime.securesms.attachments.AttachmentId; +import org.thoughtcrime.securesms.backup.proto.Attachment; +import org.thoughtcrime.securesms.backup.proto.Avatar; +import org.thoughtcrime.securesms.backup.proto.BackupFrame; +import org.thoughtcrime.securesms.backup.proto.DatabaseVersion; +import org.thoughtcrime.securesms.backup.proto.Header; +import org.thoughtcrime.securesms.backup.proto.KeyValue; +import org.thoughtcrime.securesms.backup.proto.SharedPreference; +import org.thoughtcrime.securesms.backup.proto.SqlStatement; +import org.thoughtcrime.securesms.backup.proto.Sticker; import org.thoughtcrime.securesms.crypto.AttachmentSecret; import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream; import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; @@ -80,6 +88,8 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import okio.ByteString; + public class FullBackupExporter extends FullBackupBase { private static final String TAG = Log.tag(FullBackupExporter.class); @@ -187,7 +197,7 @@ public class FullBackupExporter extends FullBackupBase { stopwatch.split("table::" + table); } - for (BackupProtos.SharedPreference preference : TextSecurePreferences.getPreferencesToSaveToBackup(context)) { + for (SharedPreference preference : TextSecurePreferences.getPreferencesToSaveToBackup(context)) { throwIfCanceled(cancellationSignal); EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount)); outputStream.write(preference); @@ -287,7 +297,7 @@ public class FullBackupExporter extends FullBackupBase { String statement = createStatementsByTable.get(table); if (statement != null) { - outputStream.write(BackupProtos.SqlStatement.newBuilder().setStatement(statement).build()); + outputStream.write(new SqlStatement.Builder().statement(statement).build()); } else { throw new IOException("Failed to find a create statement for table: " + table); } @@ -299,7 +309,7 @@ public class FullBackupExporter extends FullBackupBase { String name = cursor.getString(1); if (isTableAllowed(name)) { - outputStream.write(BackupProtos.SqlStatement.newBuilder().setStatement(sql).build()); + outputStream.write(new SqlStatement.Builder().statement(sql).build()); } } } @@ -393,8 +403,8 @@ public class FullBackupExporter extends FullBackupBase { throwIfCanceled(cancellationSignal); if (predicate == null || predicate.test(cursor)) { - StringBuilder statement = new StringBuilder(template); - BackupProtos.SqlStatement.Builder statementBuilder = BackupProtos.SqlStatement.newBuilder(); + StringBuilder statement = new StringBuilder(template); + SqlStatement.Builder statementBuilder = new SqlStatement.Builder(); statement.append('('); @@ -402,15 +412,15 @@ public class FullBackupExporter extends FullBackupBase { statement.append('?'); if (cursor.getType(i) == Cursor.FIELD_TYPE_STRING) { - statementBuilder.addParameters(BackupProtos.SqlStatement.SqlParameter.newBuilder().setStringParamter(cursor.getString(i))); + statementBuilder.parameters.add(new SqlStatement.SqlParameter.Builder().stringParamter(cursor.getString(i)).build()); } else if (cursor.getType(i) == Cursor.FIELD_TYPE_FLOAT) { - statementBuilder.addParameters(BackupProtos.SqlStatement.SqlParameter.newBuilder().setDoubleParameter(cursor.getDouble(i))); + statementBuilder.parameters.add(new SqlStatement.SqlParameter.Builder().doubleParameter(cursor.getDouble(i)).build()); } else if (cursor.getType(i) == Cursor.FIELD_TYPE_INTEGER) { - statementBuilder.addParameters(BackupProtos.SqlStatement.SqlParameter.newBuilder().setIntegerParameter(cursor.getLong(i))); + statementBuilder.parameters.add(new SqlStatement.SqlParameter.Builder().integerParameter(cursor.getLong(i)).build()); } else if (cursor.getType(i) == Cursor.FIELD_TYPE_BLOB) { - statementBuilder.addParameters(BackupProtos.SqlStatement.SqlParameter.newBuilder().setBlobParameter(ByteString.copyFrom(cursor.getBlob(i)))); + statementBuilder.parameters.add(new SqlStatement.SqlParameter.Builder().blobParameter(new ByteString(cursor.getBlob(i))).build()); } else if (cursor.getType(i) == Cursor.FIELD_TYPE_NULL) { - statementBuilder.addParameters(BackupProtos.SqlStatement.SqlParameter.newBuilder().setNullparameter(true)); + statementBuilder.parameters.add(new SqlStatement.SqlParameter.Builder().nullparameter(true).build()); } else { throw new AssertionError("unknown type?" + cursor.getType(i)); } @@ -423,7 +433,7 @@ public class FullBackupExporter extends FullBackupBase { statement.append(')'); EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount)); - outputStream.write(statementBuilder.setStatement(statement.toString()).build()); + outputStream.write(statementBuilder.statement(statement.toString()).build()); if (postProcess != null) { count = postProcess.postProcess(cursor, count); @@ -539,29 +549,30 @@ public class FullBackupExporter extends FullBackupBase { if (!dataSet.containsKey(key)) { continue; } - BackupProtos.KeyValue.Builder builder = BackupProtos.KeyValue.newBuilder() - .setKey(key); + + KeyValue.Builder builder = new KeyValue.Builder() + .key(key); Class type = dataSet.getType(key); if (type == byte[].class) { byte[] data = dataSet.getBlob(key, null); if (data != null) { - builder.setBlobValue(ByteString.copyFrom(dataSet.getBlob(key, null))); + builder.blobValue(new ByteString(dataSet.getBlob(key, null))); } else { Log.w(TAG, "Skipping storing null blob for key: " + key); } } else if (type == Boolean.class) { - builder.setBooleanValue(dataSet.getBoolean(key, false)); + builder.booleanValue(dataSet.getBoolean(key, false)); } else if (type == Float.class) { - builder.setFloatValue(dataSet.getFloat(key, 0)); + builder.floatValue(dataSet.getFloat(key, 0)); } else if (type == Integer.class) { - builder.setIntegerValue(dataSet.getInteger(key, 0)); + builder.integerValue(dataSet.getInteger(key, 0)); } else if (type == Long.class) { - builder.setLongValue(dataSet.getLong(key, 0)); + builder.longValue(dataSet.getLong(key, 0)); } else if (type == String.class) { String data = dataSet.getString(key, null); if (data != null) { - builder.setStringValue(dataSet.getString(key, null)); + builder.stringValue(dataSet.getString(key, null)); } else { Log.w(TAG, "Skipping storing null string for key: " + key); } @@ -631,10 +642,12 @@ public class FullBackupExporter extends FullBackupBase { mac.init(new SecretKeySpec(macKey, "HmacSHA256")); - byte[] header = BackupProtos.BackupFrame.newBuilder().setHeader(BackupProtos.Header.newBuilder() - .setIv(ByteString.copyFrom(iv)) - .setSalt(ByteString.copyFrom(salt))) - .build().toByteArray(); + byte[] header = new BackupFrame.Builder().header_(new Header.Builder() + .iv(new okio.ByteString(iv)) + .salt(new okio.ByteString(salt)) + .build()) + .build() + .encode(); outputStream.write(Conversions.intToByteArray(header.length)); outputStream.write(header); @@ -643,26 +656,26 @@ public class FullBackupExporter extends FullBackupBase { } } - public void write(BackupProtos.SharedPreference preference) throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder().setPreference(preference).build()); + public void write(SharedPreference preference) throws IOException { + write(outputStream, new BackupFrame.Builder().preference(preference).build()); } - public void write(BackupProtos.KeyValue keyValue) throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder().setKeyValue(keyValue).build()); + public void write(KeyValue keyValue) throws IOException { + write(outputStream, new BackupFrame.Builder().keyValue(keyValue).build()); } - public void write(BackupProtos.SqlStatement statement) throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder().setStatement(statement).build()); + public void write(SqlStatement statement) throws IOException { + write(outputStream, new BackupFrame.Builder().statement(statement).build()); } public void write(@NonNull String avatarName, @NonNull InputStream in, long size) throws IOException { try { - write(outputStream, BackupProtos.BackupFrame.newBuilder() - .setAvatar(BackupProtos.Avatar.newBuilder() - .setRecipientId(avatarName) - .setLength(Util.toIntExact(size)) - .build()) - .build()); + write(outputStream, new BackupFrame.Builder() + .avatar(new Avatar.Builder() + .recipientId(avatarName) + .length(Util.toIntExact(size)) + .build()) + .build()); } catch (ArithmeticException e) { Log.w(TAG, "Unable to write avatar to backup", e); throw new InvalidBackupStreamException(); @@ -675,13 +688,13 @@ public class FullBackupExporter extends FullBackupBase { public void write(@NonNull AttachmentId attachmentId, @NonNull InputStream in, long size) throws IOException { try { - write(outputStream, BackupProtos.BackupFrame.newBuilder() - .setAttachment(BackupProtos.Attachment.newBuilder() - .setRowId(attachmentId.getRowId()) - .setAttachmentId(attachmentId.getUniqueId()) - .setLength(Util.toIntExact(size)) - .build()) - .build()); + write(outputStream, new BackupFrame.Builder() + .attachment(new Attachment.Builder() + .rowId(attachmentId.getRowId()) + .attachmentId(attachmentId.getUniqueId()) + .length(Util.toIntExact(size)) + .build()) + .build()); } catch (ArithmeticException e) { Log.w(TAG, "Unable to write " + attachmentId + " to backup", e); throw new InvalidBackupStreamException(); @@ -694,12 +707,12 @@ public class FullBackupExporter extends FullBackupBase { public void writeSticker(long rowId, @NonNull InputStream in, long size) throws IOException { try { - write(outputStream, BackupProtos.BackupFrame.newBuilder() - .setSticker(BackupProtos.Sticker.newBuilder() - .setRowId(rowId) - .setLength(Util.toIntExact(size)) - .build()) - .build()); + write(outputStream, new BackupFrame.Builder() + .sticker(new Sticker.Builder() + .rowId(rowId) + .length(Util.toIntExact(size)) + .build()) + .build()); } catch (ArithmeticException e) { Log.w(TAG, "Unable to write sticker to backup", e); throw new InvalidBackupStreamException(); @@ -711,13 +724,13 @@ public class FullBackupExporter extends FullBackupBase { } void writeDatabaseVersion(int version) throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder() - .setVersion(BackupProtos.DatabaseVersion.newBuilder().setVersion(version)) - .build()); + write(outputStream, new BackupFrame.Builder() + .version(new DatabaseVersion.Builder().version(version).build()) + .build()); } void writeEnd() throws IOException { - write(outputStream, BackupProtos.BackupFrame.newBuilder().setEnd(true).build()); + write(outputStream, new BackupFrame.Builder().end(true).build()); } /** @@ -758,12 +771,12 @@ public class FullBackupExporter extends FullBackupBase { } } - private void write(@NonNull OutputStream out, @NonNull BackupProtos.BackupFrame frame) throws IOException { + private void write(@NonNull OutputStream out, @NonNull BackupFrame frame) throws IOException { try { Conversions.intToByteArray(iv, 0, counter++); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(cipherKey, "AES"), new IvParameterSpec(iv)); - byte[] frameCiphertext = cipher.doFinal(frame.toByteArray()); + byte[] frameCiphertext = cipher.doFinal(frame.encode()); byte[] frameMac = mac.doFinal(frameCiphertext); byte[] length = Conversions.intToByteArray(frameCiphertext.length + 10); diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java index b2a27bc09..9462de450 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java @@ -17,12 +17,14 @@ import net.zetetic.database.sqlcipher.SQLiteDatabase; import org.greenrobot.eventbus.EventBus; import org.signal.core.util.SqlUtil; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.backup.BackupProtos.Attachment; -import org.thoughtcrime.securesms.backup.BackupProtos.BackupFrame; -import org.thoughtcrime.securesms.backup.BackupProtos.DatabaseVersion; -import org.thoughtcrime.securesms.backup.BackupProtos.SharedPreference; -import org.thoughtcrime.securesms.backup.BackupProtos.SqlStatement; -import org.thoughtcrime.securesms.backup.BackupProtos.Sticker; +import org.thoughtcrime.securesms.backup.proto.Attachment; +import org.thoughtcrime.securesms.backup.proto.Avatar; +import org.thoughtcrime.securesms.backup.proto.BackupFrame; +import org.thoughtcrime.securesms.backup.proto.DatabaseVersion; +import org.thoughtcrime.securesms.backup.proto.KeyValue; +import org.thoughtcrime.securesms.backup.proto.SharedPreference; +import org.thoughtcrime.securesms.backup.proto.SqlStatement; +import org.thoughtcrime.securesms.backup.proto.Sticker; import org.thoughtcrime.securesms.crypto.AttachmentSecret; import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream; import org.thoughtcrime.securesms.database.AttachmentTable; @@ -87,17 +89,17 @@ public class FullBackupImporter extends FullBackupBase { BackupFrame frame; - while (!(frame = inputStream.readFrame()).getEnd()) { + while ((frame = inputStream.readFrame()).end != Boolean.TRUE) { if (count % 100 == 0) EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, count, 0)); count++; - if (frame.hasVersion()) processVersion(db, frame.getVersion()); - else if (frame.hasStatement()) tryProcessStatement(db, frame.getStatement()); - 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, db, frame.getAvatar(), inputStream); - else if (frame.hasKeyValue()) processKeyValue(frame.getKeyValue()); + if (frame.version != null) processVersion(db, frame.version); + else if (frame.statement != null) tryProcessStatement(db, frame.statement); + else if (frame.preference != null) processPreference(context, frame.preference); + else if (frame.attachment != null) processAttachment(context, attachmentSecret, db, frame.attachment, inputStream); + else if (frame.sticker != null) processSticker(context, attachmentSecret, db, frame.sticker, inputStream); + else if (frame.avatar != null) processAvatar(context, db, frame.avatar, inputStream); + else if (frame.keyValue != null) processKeyValue(frame.keyValue); else count--; } @@ -120,11 +122,11 @@ public class FullBackupImporter extends FullBackupBase { } private static void processVersion(@NonNull SQLiteDatabase db, DatabaseVersion version) throws IOException { - if (version.getVersion() > db.getVersion()) { - throw new DatabaseDowngradeException(db.getVersion(), version.getVersion()); + if (version.version == null || version.version > db.getVersion()) { + throw new DatabaseDowngradeException(db.getVersion(), version.version != null ? version.version : -1); } - db.setVersion(version.getVersion()); + db.setVersion(version.version); } private static void tryProcessStatement(@NonNull SQLiteDatabase db, SqlStatement statement) { @@ -132,9 +134,9 @@ public class FullBackupImporter extends FullBackupBase { processStatement(db, statement); } catch (SQLiteConstraintException e) { String tableName = "?"; - String statementString = statement.getStatement(); + String statementString = statement.statement; - if (statementString.startsWith("INSERT INTO ")) { + if (statementString != null && statementString.startsWith("INSERT INTO ")) { int nameStart = "INSERT INTO ".length(); int nameEnd = statementString.indexOf(" ", "INSERT INTO ".length()); @@ -153,27 +155,32 @@ public class FullBackupImporter extends FullBackupBase { } private static void processStatement(@NonNull SQLiteDatabase db, SqlStatement statement) { - boolean isForMmsFtsSecretTable = statement.getStatement().contains(SearchTable.FTS_TABLE_NAME + "_"); - boolean isForEmojiSecretTable = statement.getStatement().contains(EmojiSearchTable.TABLE_NAME + "_"); - boolean isForSqliteSecretTable = statement.getStatement().toLowerCase().startsWith("create table sqlite_"); + if (statement.statement == null) { + Log.w(TAG, "Null statement!"); + return; + } + + boolean isForMmsFtsSecretTable = statement.statement.contains(SearchTable.FTS_TABLE_NAME + "_"); + boolean isForEmojiSecretTable = statement.statement.contains(EmojiSearchTable.TABLE_NAME + "_"); + boolean isForSqliteSecretTable = statement.statement.toLowerCase().startsWith("create table sqlite_"); if (isForMmsFtsSecretTable || isForEmojiSecretTable || isForSqliteSecretTable) { - Log.i(TAG, "Ignoring import for statement: " + statement.getStatement()); + Log.i(TAG, "Ignoring import for statement: " + statement.statement); return; } List parameters = new LinkedList<>(); - for (SqlStatement.SqlParameter parameter : statement.getParametersList()) { - if (parameter.hasStringParamter()) parameters.add(parameter.getStringParamter()); - else if (parameter.hasDoubleParameter()) parameters.add(parameter.getDoubleParameter()); - else if (parameter.hasIntegerParameter()) parameters.add(parameter.getIntegerParameter()); - else if (parameter.hasBlobParameter()) parameters.add(parameter.getBlobParameter().toByteArray()); - else if (parameter.hasNullparameter()) parameters.add(null); + for (SqlStatement.SqlParameter parameter : statement.parameters) { + if (parameter.stringParamter != null) parameters.add(parameter.stringParamter); + else if (parameter.doubleParameter != null) parameters.add(parameter.doubleParameter); + else if (parameter.integerParameter != null) parameters.add(parameter.integerParameter); + else if (parameter.blobParameter != null) parameters.add(parameter.blobParameter.toByteArray()); + else if (parameter.nullparameter != null) parameters.add(null); } - if (parameters.size() > 0) db.execSQL(statement.getStatement(), parameters.toArray()); - else db.execSQL(statement.getStatement()); + if (parameters.size() > 0) db.execSQL(statement.statement, parameters.toArray()); + else db.execSQL(statement.statement); } private static void processAttachment(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret, @NonNull SQLiteDatabase db, @NonNull Attachment attachment, BackupRecordInputStream inputStream) @@ -185,12 +192,12 @@ public class FullBackupImporter extends FullBackupBase { ContentValues contentValues = new ContentValues(); try { - inputStream.readAttachmentTo(output.second, attachment.getLength()); + inputStream.readAttachmentTo(output.second, attachment.length); contentValues.put(AttachmentTable.DATA, dataFile.getAbsolutePath()); contentValues.put(AttachmentTable.DATA_RANDOM, output.first); } catch (BackupRecordInputStream.BadMacException e) { - Log.w(TAG, "Bad MAC for attachment " + attachment.getAttachmentId() + "! Can't restore it.", e); + Log.w(TAG, "Bad MAC for attachment " + attachment.attachmentId + "! Can't restore it.", e); dataFile.delete(); contentValues.put(AttachmentTable.DATA, (String) null); contentValues.put(AttachmentTable.DATA_RANDOM, (String) null); @@ -198,7 +205,7 @@ public class FullBackupImporter extends FullBackupBase { db.update(AttachmentTable.TABLE_NAME, contentValues, AttachmentTable.ROW_ID + " = ? AND " + AttachmentTable.UNIQUE_ID + " = ?", - new String[] {String.valueOf(attachment.getRowId()), String.valueOf(attachment.getAttachmentId())}); + new String[] {String.valueOf(attachment.rowId), String.valueOf(attachment.attachmentId)}); } private static void processSticker(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret, @NonNull SQLiteDatabase db, @NonNull Sticker sticker, BackupRecordInputStream inputStream) @@ -209,52 +216,57 @@ public class FullBackupImporter extends FullBackupBase { Pair output = ModernEncryptingPartOutputStream.createFor(attachmentSecret, dataFile, false); - inputStream.readAttachmentTo(output.second, sticker.getLength()); + inputStream.readAttachmentTo(output.second, sticker.length); ContentValues contentValues = new ContentValues(); contentValues.put(StickerTable.FILE_PATH, dataFile.getAbsolutePath()); - contentValues.put(StickerTable.FILE_LENGTH, sticker.getLength()); + contentValues.put(StickerTable.FILE_LENGTH, sticker.length); contentValues.put(StickerTable.FILE_RANDOM, output.first); db.update(StickerTable.TABLE_NAME, contentValues, StickerTable._ID + " = ?", - new String[] {String.valueOf(sticker.getRowId())}); + new String[] {String.valueOf(sticker.rowId)}); } - 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(AvatarHelper.getOutputStream(context, recipientId, false), avatar.getLength()); + private static void processAvatar(@NonNull Context context, @NonNull SQLiteDatabase db, @NonNull Avatar avatar, @NonNull BackupRecordInputStream inputStream) throws IOException { + if (avatar.recipientId != null) { + RecipientId recipientId = RecipientId.from(avatar.recipientId); + inputStream.readAttachmentTo(AvatarHelper.getOutputStream(context, recipientId, false), avatar.length); } else { - if (avatar.hasName() && SqlUtil.tableExists(db, "recipient_preferences")) { + if (avatar.name != null && 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")) { + db.execSQL("UPDATE recipient_preferences SET signal_profile_avatar = NULL WHERE recipient_ids = ?", new String[] { avatar.name }); + } else if (avatar.name != null && 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() }); + db.execSQL("UPDATE recipient SET signal_profile_avatar = NULL WHERE phone = ?", new String[] { avatar.name }); } else { Log.w(TAG, "Avatar is missing a recipientId. Skipping avatar restore."); } - inputStream.readAttachmentTo(new ByteArrayOutputStream(), avatar.getLength()); + inputStream.readAttachmentTo(new ByteArrayOutputStream(), avatar.length); } } - private static void processKeyValue(BackupProtos.KeyValue keyValue) { + private static void processKeyValue(KeyValue keyValue) { KeyValueDataSet dataSet = new KeyValueDataSet(); - if (keyValue.hasBlobValue()) { - dataSet.putBlob(keyValue.getKey(), keyValue.getBlobValue().toByteArray()); - } else if (keyValue.hasBooleanValue()) { - dataSet.putBoolean(keyValue.getKey(), keyValue.getBooleanValue()); - } else if (keyValue.hasFloatValue()) { - dataSet.putFloat(keyValue.getKey(), keyValue.getFloatValue()); - } else if (keyValue.hasIntegerValue()) { - dataSet.putInteger(keyValue.getKey(), keyValue.getIntegerValue()); - } else if (keyValue.hasLongValue()) { - dataSet.putLong(keyValue.getKey(), keyValue.getLongValue()); - } else if (keyValue.hasStringValue()) { - dataSet.putString(keyValue.getKey(), keyValue.getStringValue()); + if (keyValue.key == null) { + Log.w(TAG, "Null preference key!"); + return; + } + + if (keyValue.blobValue != null) { + dataSet.putBlob(keyValue.key, keyValue.blobValue.toByteArray()); + } else if (keyValue.booleanValue != null) { + dataSet.putBoolean(keyValue.key, keyValue.booleanValue); + } else if (keyValue.floatValue != null) { + dataSet.putFloat(keyValue.key, keyValue.floatValue); + } else if (keyValue.integerValue != null) { + dataSet.putInteger(keyValue.key, keyValue.integerValue); + } else if (keyValue.longValue != null) { + dataSet.putLong(keyValue.key, keyValue.longValue); + } else if (keyValue.stringValue != null) { + dataSet.putString(keyValue.key, keyValue.stringValue); } else { Log.i(TAG, "Unknown KeyValue backup value, skipping"); return; @@ -265,25 +277,25 @@ public class FullBackupImporter extends FullBackupBase { @SuppressLint("ApplySharedPref") private static void processPreference(@NonNull Context context, SharedPreference preference) { - SharedPreferences preferences = context.getSharedPreferences(preference.getFile(), 0); + SharedPreferences preferences = context.getSharedPreferences(preference.file_, 0); // Identity keys were moved from shared prefs into SignalStore. Need to handle importing backups made before the migration. - if ("SecureSMS-Preferences".equals(preference.getFile())) { - if ("pref_identity_public_v3".equals(preference.getKey()) && preference.hasValue()) { - SignalStore.account().restoreLegacyIdentityPublicKeyFromBackup(preference.getValue()); - } else if ("pref_identity_private_v3".equals(preference.getKey()) && preference.hasValue()) { - SignalStore.account().restoreLegacyIdentityPrivateKeyFromBackup(preference.getValue()); + if ("SecureSMS-Preferences".equals(preference.file_)) { + if ("pref_identity_public_v3".equals(preference.key) && preference.value_ != null) { + SignalStore.account().restoreLegacyIdentityPublicKeyFromBackup(preference.value_); + } else if ("pref_identity_private_v3".equals(preference.key) && preference.value_ != null) { + SignalStore.account().restoreLegacyIdentityPrivateKeyFromBackup(preference.value_); } return; } - if (preference.hasValue()) { - preferences.edit().putString(preference.getKey(), preference.getValue()).commit(); - } else if (preference.hasBooleanValue()) { - preferences.edit().putBoolean(preference.getKey(), preference.getBooleanValue()).commit(); - } else if (preference.hasIsStringSetValue() && preference.getIsStringSetValue()) { - preferences.edit().putStringSet(preference.getKey(), new HashSet<>(preference.getStringSetValueList())).commit(); + if (preference.value_ != null) { + preferences.edit().putString(preference.key, preference.value_).commit(); + } else if (preference.booleanValue != null) { + preferences.edit().putBoolean(preference.key, preference.booleanValue).commit(); + } else if (preference.isStringSetValue == Boolean.TRUE) { + preferences.edit().putStringSet(preference.key, new HashSet<>(preference.stringSetValue)).commit(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 2c9d7205b..3eadc6061 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -20,11 +20,10 @@ import org.signal.core.util.PendingIntentFlags; import org.signal.core.util.logging.Log; import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.backup.BackupProtos; +import org.thoughtcrime.securesms.backup.proto.SharedPreference; import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.events.ReminderUpdateEvent; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; import org.thoughtcrime.securesms.keyvalue.SettingsValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; @@ -253,39 +252,39 @@ public class TextSecurePreferences { return count; } - public static List getPreferencesToSaveToBackup(@NonNull Context context) { - SharedPreferences preferences = getSharedPreferences(context); - List backupProtos = new ArrayList<>(); - String defaultFile = context.getPackageName() + "_preferences"; + public static List getPreferencesToSaveToBackup(@NonNull Context context) { + SharedPreferences preferences = getSharedPreferences(context); + List backupProtos = new ArrayList<>(); + String defaultFile = context.getPackageName() + "_preferences"; for (String booleanPreference : booleanPreferencesToBackup) { if (preferences.contains(booleanPreference)) { - backupProtos.add(BackupProtos.SharedPreference.newBuilder() - .setFile(defaultFile) - .setKey(booleanPreference) - .setBooleanValue(preferences.getBoolean(booleanPreference, false)) - .build()); + backupProtos.add(new SharedPreference.Builder() + .file_(defaultFile) + .key(booleanPreference) + .booleanValue(preferences.getBoolean(booleanPreference, false)) + .build()); } } for (String stringPreference : stringPreferencesToBackup) { if (preferences.contains(stringPreference)) { - backupProtos.add(BackupProtos.SharedPreference.newBuilder() - .setFile(defaultFile) - .setKey(stringPreference) - .setValue(preferences.getString(stringPreference, null)) - .build()); + backupProtos.add(new SharedPreference.Builder() + .file_(defaultFile) + .key(stringPreference) + .value_(preferences.getString(stringPreference, null)) + .build()); } } for (String stringSetPreference : stringSetPreferencesToBackup) { if (preferences.contains(stringSetPreference)) { - backupProtos.add(BackupProtos.SharedPreference.newBuilder() - .setFile(defaultFile) - .setKey(stringSetPreference) - .setIsStringSetValue(true) - .addAllStringSetValue(preferences.getStringSet(stringSetPreference, Collections.emptySet())) - .build()); + backupProtos.add(new SharedPreference.Builder() + .file_(defaultFile) + .key(stringSetPreference) + .isStringSetValue(true) + .stringSetValue(new ArrayList<>(preferences.getStringSet(stringSetPreference, Collections.emptySet()))) + .build()); } } diff --git a/app/src/main/proto/Backups.proto b/app/src/main/protowire/Backups.proto similarity index 94% rename from app/src/main/proto/Backups.proto rename to app/src/main/protowire/Backups.proto index 41acb55d5..963206887 100644 --- a/app/src/main/proto/Backups.proto +++ b/app/src/main/protowire/Backups.proto @@ -8,8 +8,7 @@ syntax = "proto2"; package signal; -option java_package = "org.thoughtcrime.securesms.backup"; -option java_outer_classname = "BackupProtos"; +option java_package = "org.thoughtcrime.securesms.backup.proto"; message SqlStatement { message SqlParameter {