Move Backups.proto to Wire.

main
Greyson Parrelli 2023-02-11 12:48:42 -05:00
rodzic 055ceba398
commit af6f16bdb6
6 zmienionych plików z 197 dodań i 169 usunięć

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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);

Wyświetl plik

@ -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<Object> 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<byte[], OutputStream> 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();
}
}

Wyświetl plik

@ -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<BackupProtos.SharedPreference> getPreferencesToSaveToBackup(@NonNull Context context) {
SharedPreferences preferences = getSharedPreferences(context);
List<BackupProtos.SharedPreference> backupProtos = new ArrayList<>();
String defaultFile = context.getPackageName() + "_preferences";
public static List<SharedPreference> getPreferencesToSaveToBackup(@NonNull Context context) {
SharedPreferences preferences = getSharedPreferences(context);
List<SharedPreference> 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());
}
}

Wyświetl plik

@ -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 {