mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 09:20:19 +01:00
Support PNI prekeys.
This commit is contained in:
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -17,6 +18,7 @@ import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet;
|
||||
import org.thoughtcrime.securesms.keyvalue.KeyValuePersistentStorage;
|
||||
import org.thoughtcrime.securesms.util.CursorUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -60,6 +62,11 @@ public class KeyValueDatabase extends SQLiteOpenHelper implements SignalDatabase
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static boolean exists(Context context) {
|
||||
return context.getDatabasePath(DATABASE_NAME).exists();
|
||||
}
|
||||
|
||||
|
||||
private KeyValueDatabase(@NonNull Application application, @NonNull DatabaseSecret databaseSecret) {
|
||||
super(application, DATABASE_NAME, databaseSecret.asString(), null, DATABASE_VERSION, 0,new SqlCipherErrorHandler(DATABASE_NAME), new SqlCipherDatabaseHook());
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.whispersystems.libsignal.InvalidKeyException;
|
||||
import org.whispersystems.libsignal.ecc.Curve;
|
||||
import org.whispersystems.libsignal.ecc.ECKeyPair;
|
||||
import org.whispersystems.libsignal.ecc.ECPrivateKey;
|
||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class OneTimePreKeyDatabase extends Database {
|
||||
|
||||
private static final String TAG = Log.tag(OneTimePreKeyDatabase.class);
|
||||
|
||||
public static final String TABLE_NAME = "one_time_prekeys";
|
||||
private static final String ID = "_id";
|
||||
public static final String KEY_ID = "key_id";
|
||||
public static final String PUBLIC_KEY = "public_key";
|
||||
public static final String PRIVATE_KEY = "private_key";
|
||||
|
||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
|
||||
" (" + ID + " INTEGER PRIMARY KEY, " +
|
||||
KEY_ID + " INTEGER UNIQUE, " +
|
||||
PUBLIC_KEY + " TEXT NOT NULL, " +
|
||||
PRIVATE_KEY + " TEXT NOT NULL);";
|
||||
|
||||
OneTimePreKeyDatabase(Context context, SignalDatabase databaseHelper) {
|
||||
super(context, databaseHelper);
|
||||
}
|
||||
|
||||
public @Nullable PreKeyRecord getPreKey(int keyId) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
|
||||
try (Cursor cursor = database.query(TABLE_NAME, null, KEY_ID + " = ?",
|
||||
new String[] {String.valueOf(keyId)},
|
||||
null, null, null))
|
||||
{
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
try {
|
||||
ECPublicKey publicKey = Curve.decodePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PUBLIC_KEY))), 0);
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PRIVATE_KEY))));
|
||||
|
||||
return new PreKeyRecord(keyId, new ECKeyPair(publicKey, privateKey));
|
||||
} catch (InvalidKeyException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void insertPreKey(int keyId, PreKeyRecord record) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
|
||||
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(KEY_ID, keyId);
|
||||
contentValues.put(PUBLIC_KEY, Base64.encodeBytes(record.getKeyPair().getPublicKey().serialize()));
|
||||
contentValues.put(PRIVATE_KEY, Base64.encodeBytes(record.getKeyPair().getPrivateKey().serialize()));
|
||||
|
||||
database.replace(TABLE_NAME, null, contentValues);
|
||||
}
|
||||
|
||||
public void removePreKey(int keyId) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
|
||||
database.delete(TABLE_NAME, KEY_ID + " = ?", new String[] {String.valueOf(keyId)});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.contentValuesOf
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.util.Base64
|
||||
import org.thoughtcrime.securesms.util.SqlUtil
|
||||
import org.whispersystems.libsignal.InvalidKeyException
|
||||
import org.whispersystems.libsignal.ecc.Curve
|
||||
import org.whispersystems.libsignal.ecc.ECKeyPair
|
||||
import org.whispersystems.libsignal.state.PreKeyRecord
|
||||
import org.whispersystems.signalservice.api.push.AccountIdentifier
|
||||
import java.io.IOException
|
||||
|
||||
class OneTimePreKeyDatabase internal constructor(context: Context?, databaseHelper: SignalDatabase?) : Database(context, databaseHelper) {
|
||||
companion object {
|
||||
private val TAG = Log.tag(OneTimePreKeyDatabase::class.java)
|
||||
|
||||
const val TABLE_NAME = "one_time_prekeys"
|
||||
const val ID = "_id"
|
||||
const val ACCOUNT_ID = "account_id"
|
||||
const val KEY_ID = "key_id"
|
||||
const val PUBLIC_KEY = "public_key"
|
||||
const val PRIVATE_KEY = "private_key"
|
||||
const val CREATE_TABLE = """
|
||||
CREATE TABLE $TABLE_NAME (
|
||||
$ID INTEGER PRIMARY KEY,
|
||||
$ACCOUNT_ID TEXT NOT NULL,
|
||||
$KEY_ID INTEGER UNIQUE,
|
||||
$PUBLIC_KEY TEXT NOT NULL,
|
||||
$PRIVATE_KEY TEXT NOT NULL,
|
||||
UNIQUE($ACCOUNT_ID, $KEY_ID)
|
||||
)
|
||||
"""
|
||||
}
|
||||
|
||||
fun get(accountId: AccountIdentifier, keyId: Int): PreKeyRecord? {
|
||||
readableDatabase.query(TABLE_NAME, null, "$ACCOUNT_ID = ? AND $KEY_ID = ?", SqlUtil.buildArgs(accountId, keyId), null, null, null).use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
try {
|
||||
val publicKey = Curve.decodePoint(Base64.decode(cursor.requireNonNullString(PUBLIC_KEY)), 0)
|
||||
val privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.requireNonNullString(PRIVATE_KEY)))
|
||||
return PreKeyRecord(keyId, ECKeyPair(publicKey, privateKey))
|
||||
} catch (e: InvalidKeyException) {
|
||||
Log.w(TAG, e)
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun insert(accountId: AccountIdentifier, keyId: Int, record: PreKeyRecord) {
|
||||
val contentValues = contentValuesOf(
|
||||
ACCOUNT_ID to accountId.toString(),
|
||||
KEY_ID to keyId,
|
||||
PUBLIC_KEY to Base64.encodeBytes(record.keyPair.publicKey.serialize()),
|
||||
PRIVATE_KEY to Base64.encodeBytes(record.keyPair.privateKey.serialize())
|
||||
)
|
||||
|
||||
writableDatabase.replace(TABLE_NAME, null, contentValues)
|
||||
}
|
||||
|
||||
fun delete(accountId: AccountIdentifier, keyId: Int) {
|
||||
val database = databaseHelper.signalWritableDatabase
|
||||
database.delete(TABLE_NAME, "$ACCOUNT_ID = ? AND $KEY_ID = ?", SqlUtil.buildArgs(accountId, keyId))
|
||||
}
|
||||
}
|
||||
@@ -397,8 +397,8 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
|
||||
get() = instance!!.pendingRetryReceiptDatabase
|
||||
|
||||
@get:JvmStatic
|
||||
@get:JvmName("preKeys")
|
||||
val preKeys: OneTimePreKeyDatabase
|
||||
@get:JvmName("oneTimePreKeys")
|
||||
val oneTimePreKeys: OneTimePreKeyDatabase
|
||||
get() = instance!!.preKeyDatabase
|
||||
|
||||
@get:Deprecated("This only exists to migrate from legacy storage. There shouldn't be any new usages.")
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.whispersystems.libsignal.InvalidKeyException;
|
||||
import org.whispersystems.libsignal.ecc.Curve;
|
||||
import org.whispersystems.libsignal.ecc.ECKeyPair;
|
||||
import org.whispersystems.libsignal.ecc.ECPrivateKey;
|
||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class SignedPreKeyDatabase extends Database {
|
||||
|
||||
private static final String TAG = Log.tag(SignedPreKeyDatabase.class);
|
||||
|
||||
public static final String TABLE_NAME = "signed_prekeys";
|
||||
|
||||
private static final String ID = "_id";
|
||||
public static final String KEY_ID = "key_id";
|
||||
public static final String PUBLIC_KEY = "public_key";
|
||||
public static final String PRIVATE_KEY = "private_key";
|
||||
public static final String SIGNATURE = "signature";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
|
||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME +
|
||||
" (" + ID + " INTEGER PRIMARY KEY, " +
|
||||
KEY_ID + " INTEGER UNIQUE, " +
|
||||
PUBLIC_KEY + " TEXT NOT NULL, " +
|
||||
PRIVATE_KEY + " TEXT NOT NULL, " +
|
||||
SIGNATURE + " TEXT NOT NULL, " +
|
||||
TIMESTAMP + " INTEGER DEFAULT 0);";
|
||||
|
||||
SignedPreKeyDatabase(Context context, SignalDatabase databaseHelper) {
|
||||
super(context, databaseHelper);
|
||||
}
|
||||
|
||||
public @Nullable SignedPreKeyRecord getSignedPreKey(int keyId) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
|
||||
try (Cursor cursor = database.query(TABLE_NAME, null, KEY_ID + " = ?",
|
||||
new String[] {String.valueOf(keyId)},
|
||||
null, null, null))
|
||||
{
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
try {
|
||||
ECPublicKey publicKey = Curve.decodePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PUBLIC_KEY))), 0);
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PRIVATE_KEY))));
|
||||
byte[] signature = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(SIGNATURE)));
|
||||
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
|
||||
|
||||
return new SignedPreKeyRecord(keyId, timestamp, new ECKeyPair(publicKey, privateKey), signature);
|
||||
} catch (InvalidKeyException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public @NonNull List<SignedPreKeyRecord> getAllSignedPreKeys() {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
List<SignedPreKeyRecord> results = new LinkedList<>();
|
||||
|
||||
try (Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
try {
|
||||
int keyId = cursor.getInt(cursor.getColumnIndexOrThrow(KEY_ID));
|
||||
ECPublicKey publicKey = Curve.decodePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PUBLIC_KEY))), 0);
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(PRIVATE_KEY))));
|
||||
byte[] signature = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(SIGNATURE)));
|
||||
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
|
||||
|
||||
results.add(new SignedPreKeyRecord(keyId, timestamp, new ECKeyPair(publicKey, privateKey), signature));
|
||||
} catch (InvalidKeyException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public void insertSignedPreKey(int keyId, SignedPreKeyRecord record) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
|
||||
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(KEY_ID, keyId);
|
||||
contentValues.put(PUBLIC_KEY, Base64.encodeBytes(record.getKeyPair().getPublicKey().serialize()));
|
||||
contentValues.put(PRIVATE_KEY, Base64.encodeBytes(record.getKeyPair().getPrivateKey().serialize()));
|
||||
contentValues.put(SIGNATURE, Base64.encodeBytes(record.getSignature()));
|
||||
contentValues.put(TIMESTAMP, record.getTimestamp());
|
||||
|
||||
database.replace(TABLE_NAME, null, contentValues);
|
||||
}
|
||||
|
||||
|
||||
public void removeSignedPreKey(int keyId) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
|
||||
database.delete(TABLE_NAME, KEY_ID + " = ? AND " + SIGNATURE + " IS NOT NULL", new String[] {String.valueOf(keyId)});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.contentValuesOf
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.util.Base64
|
||||
import org.thoughtcrime.securesms.util.SqlUtil
|
||||
import org.whispersystems.libsignal.InvalidKeyException
|
||||
import org.whispersystems.libsignal.ecc.Curve
|
||||
import org.whispersystems.libsignal.ecc.ECKeyPair
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord
|
||||
import org.whispersystems.signalservice.api.push.AccountIdentifier
|
||||
import java.io.IOException
|
||||
import java.util.LinkedList
|
||||
|
||||
class SignedPreKeyDatabase internal constructor(context: Context?, databaseHelper: SignalDatabase?) : Database(context, databaseHelper) {
|
||||
companion object {
|
||||
private val TAG = Log.tag(SignedPreKeyDatabase::class.java)
|
||||
|
||||
const val TABLE_NAME = "signed_prekeys"
|
||||
const val ID = "_id"
|
||||
const val ACCOUNT_ID = "account_id"
|
||||
const val KEY_ID = "key_id"
|
||||
const val PUBLIC_KEY = "public_key"
|
||||
const val PRIVATE_KEY = "private_key"
|
||||
const val SIGNATURE = "signature"
|
||||
const val TIMESTAMP = "timestamp"
|
||||
const val CREATE_TABLE = """
|
||||
CREATE TABLE $TABLE_NAME (
|
||||
$ID INTEGER PRIMARY KEY,
|
||||
$ACCOUNT_ID TEXT NOT NULL,
|
||||
$KEY_ID INTEGER UNIQUE,
|
||||
$PUBLIC_KEY TEXT NOT NULL,
|
||||
$PRIVATE_KEY TEXT NOT NULL,
|
||||
$SIGNATURE TEXT NOT NULL,
|
||||
$TIMESTAMP INTEGER DEFAULT 0,
|
||||
UNIQUE($ACCOUNT_ID, $KEY_ID)
|
||||
)
|
||||
"""
|
||||
}
|
||||
|
||||
fun get(accountId: AccountIdentifier, keyId: Int): SignedPreKeyRecord? {
|
||||
readableDatabase.query(TABLE_NAME, null, "$ACCOUNT_ID = ? AND $KEY_ID = ?", SqlUtil.buildArgs(accountId, keyId), null, null, null).use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
try {
|
||||
val publicKey = Curve.decodePoint(Base64.decode(cursor.requireNonNullString(PUBLIC_KEY)), 0)
|
||||
val privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.requireNonNullString(PRIVATE_KEY)))
|
||||
val signature = Base64.decode(cursor.requireNonNullString(SIGNATURE))
|
||||
val timestamp = cursor.requireLong(TIMESTAMP)
|
||||
return SignedPreKeyRecord(keyId, timestamp, ECKeyPair(publicKey, privateKey), signature)
|
||||
} catch (e: InvalidKeyException) {
|
||||
Log.w(TAG, e)
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getAll(accountId: AccountIdentifier): List<SignedPreKeyRecord> {
|
||||
val results: MutableList<SignedPreKeyRecord> = LinkedList()
|
||||
|
||||
readableDatabase.query(TABLE_NAME, null, "$ACCOUNT_ID = ?", SqlUtil.buildArgs(accountId), null, null, null).use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
try {
|
||||
val keyId = cursor.requireInt(KEY_ID)
|
||||
val publicKey = Curve.decodePoint(Base64.decode(cursor.requireNonNullString(PUBLIC_KEY)), 0)
|
||||
val privateKey = Curve.decodePrivatePoint(Base64.decode(cursor.requireNonNullString(PRIVATE_KEY)))
|
||||
val signature = Base64.decode(cursor.requireNonNullString(SIGNATURE))
|
||||
val timestamp = cursor.requireLong(TIMESTAMP)
|
||||
results.add(SignedPreKeyRecord(keyId, timestamp, ECKeyPair(publicKey, privateKey), signature))
|
||||
} catch (e: InvalidKeyException) {
|
||||
Log.w(TAG, e)
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
fun insert(accountId: AccountIdentifier, keyId: Int, record: SignedPreKeyRecord) {
|
||||
val contentValues = contentValuesOf(
|
||||
ACCOUNT_ID to accountId.toString(),
|
||||
KEY_ID to keyId,
|
||||
PUBLIC_KEY to Base64.encodeBytes(record.keyPair.publicKey.serialize()),
|
||||
PRIVATE_KEY to Base64.encodeBytes(record.keyPair.privateKey.serialize()),
|
||||
SIGNATURE to Base64.encodeBytes(record.signature),
|
||||
TIMESTAMP to record.timestamp
|
||||
)
|
||||
writableDatabase.replace(TABLE_NAME, null, contentValues)
|
||||
}
|
||||
|
||||
fun delete(accountId: AccountIdentifier, keyId: Int) {
|
||||
writableDatabase.delete(TABLE_NAME, "$ACCOUNT_ID = ? AND $KEY_ID = ?", SqlUtil.buildArgs(accountId, keyId))
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import org.signal.core.util.Conversions;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase;
|
||||
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
@@ -94,7 +95,7 @@ public final class PreKeyMigrationHelper {
|
||||
reader.close();
|
||||
|
||||
Log.i(TAG, "Setting next prekey id: " + index.nextPreKeyId);
|
||||
TextSecurePreferences.setNextPreKeyId(context, index.nextPreKeyId);
|
||||
SignalStore.account().aciPreKeys().setNextOneTimePreKeyId(index.nextPreKeyId);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
@@ -108,8 +109,8 @@ public final class PreKeyMigrationHelper {
|
||||
|
||||
Log.i(TAG, "Setting next signed prekey id: " + index.nextSignedPreKeyId);
|
||||
Log.i(TAG, "Setting active signed prekey id: " + index.activeSignedPreKeyId);
|
||||
TextSecurePreferences.setNextSignedPreKeyId(context, index.nextSignedPreKeyId);
|
||||
TextSecurePreferences.setActiveSignedPreKeyId(context, index.activeSignedPreKeyId);
|
||||
SignalStore.account().aciPreKeys().setNextSignedPreKeyId(index.nextSignedPreKeyId);
|
||||
SignalStore.account().aciPreKeys().setActiveSignedPreKeyId(index.activeSignedPreKeyId);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms.database.helpers
|
||||
|
||||
import android.app.Application
|
||||
import android.app.NotificationChannel
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
@@ -18,8 +19,10 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactColorsLegacy
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper.entrySet
|
||||
import org.thoughtcrime.securesms.database.KeyValueDatabase
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList
|
||||
import org.thoughtcrime.securesms.database.requireString
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob
|
||||
@@ -39,6 +42,7 @@ import org.thoughtcrime.securesms.util.SqlUtil
|
||||
import org.thoughtcrime.securesms.util.Stopwatch
|
||||
import org.thoughtcrime.securesms.util.Triple
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.api.push.ACI
|
||||
import org.whispersystems.signalservice.api.push.DistributionId
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.File
|
||||
@@ -185,11 +189,12 @@ object SignalDatabaseMigrations {
|
||||
private const val PNI_CLEANUP = 127
|
||||
private const val MESSAGE_RANGES = 128
|
||||
private const val REACTION_TRIGGER_FIX = 129
|
||||
private const val PNI_STORES = 130
|
||||
|
||||
const val DATABASE_VERSION = 129
|
||||
const val DATABASE_VERSION = 130
|
||||
|
||||
@JvmStatic
|
||||
fun migrate(context: Context, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
if (oldVersion < RECIPIENT_CALL_RINGTONE_VERSION) {
|
||||
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN call_ringtone TEXT DEFAULT NULL")
|
||||
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN call_vibrate INTEGER DEFAULT " + RecipientDatabase.VibrateState.DEFAULT.id)
|
||||
@@ -2285,6 +2290,80 @@ object SignalDatabaseMigrations {
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
if (oldVersion < PNI_STORES) {
|
||||
val localAci: ACI? = getLocalAci(context)
|
||||
|
||||
// One-Time Prekeys
|
||||
db.execSQL(
|
||||
"""
|
||||
CREATE TABLE one_time_prekeys_tmp (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
account_id TEXT NOT NULL,
|
||||
key_id INTEGER,
|
||||
public_key TEXT NOT NULL,
|
||||
private_key TEXT NOT NULL,
|
||||
UNIQUE(account_id, key_id)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
if (localAci != null) {
|
||||
db.execSQL(
|
||||
"""
|
||||
INSERT INTO one_time_prekeys_tmp (account_id, key_id, public_key, private_key)
|
||||
SELECT
|
||||
'$localAci' AS account_id,
|
||||
one_time_prekeys.key_id,
|
||||
one_time_prekeys.public_key,
|
||||
one_time_prekeys.private_key
|
||||
FROM one_time_prekeys
|
||||
""".trimIndent()
|
||||
)
|
||||
} else {
|
||||
Log.w(TAG, "No local ACI set. Not migrating any existing one-time prekeys.")
|
||||
}
|
||||
|
||||
db.execSQL("DROP TABLE one_time_prekeys")
|
||||
db.execSQL("ALTER TABLE one_time_prekeys_tmp RENAME TO one_time_prekeys")
|
||||
|
||||
// Signed Prekeys
|
||||
db.execSQL(
|
||||
"""
|
||||
CREATE TABLE signed_prekeys_tmp (
|
||||
_id INTEGER PRIMARY KEY,
|
||||
account_id TEXT NOT NULL,
|
||||
key_id INTEGER,
|
||||
public_key TEXT NOT NULL,
|
||||
private_key TEXT NOT NULL,
|
||||
signature TEXT NOT NULL,
|
||||
timestamp INTEGER DEFAULT 0,
|
||||
UNIQUE(account_id, key_id)
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
if (localAci != null) {
|
||||
db.execSQL(
|
||||
"""
|
||||
INSERT INTO signed_prekeys_tmp (account_id, key_id, public_key, private_key, signature, timestamp)
|
||||
SELECT
|
||||
'$localAci' AS account_id,
|
||||
signed_prekeys.key_id,
|
||||
signed_prekeys.public_key,
|
||||
signed_prekeys.private_key,
|
||||
signed_prekeys.signature,
|
||||
signed_prekeys.timestamp
|
||||
FROM signed_prekeys
|
||||
""".trimIndent()
|
||||
)
|
||||
} else {
|
||||
Log.w(TAG, "No local ACI set. Not migrating any existing signed prekeys.")
|
||||
}
|
||||
|
||||
db.execSQL("DROP TABLE signed_prekeys")
|
||||
db.execSQL("ALTER TABLE signed_prekeys_tmp RENAME TO signed_prekeys")
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -2294,6 +2373,9 @@ object SignalDatabaseMigrations {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Important: You can't change this method, or you risk breaking existing migrations. If you need to change this, make a new method.
|
||||
*/
|
||||
private fun migrateReaction(db: SQLiteDatabase, cursor: Cursor, isMms: Boolean) {
|
||||
try {
|
||||
val messageId = CursorUtil.requireLong(cursor, "_id")
|
||||
@@ -2314,4 +2396,22 @@ object SignalDatabaseMigrations {
|
||||
Log.w(TAG, "Failed to parse reaction!")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Important: You can't change this method, or you risk breaking existing migrations. If you need to change this, make a new method.
|
||||
*/
|
||||
private fun getLocalAci(context: Application): ACI? {
|
||||
if (KeyValueDatabase.exists(context)) {
|
||||
val keyValueDatabase = KeyValueDatabase.getInstance(context).readableDatabase
|
||||
keyValueDatabase.query("key_value", arrayOf("value"), "key = ?", SqlUtil.buildArgs("account.aci"), null, null, null).use { cursor ->
|
||||
return if (cursor.moveToFirst()) {
|
||||
ACI.parseOrNull(cursor.requireString("value"))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ACI.parseOrNull(PreferenceManager.getDefaultSharedPreferences(context).getString("pref_local_uuid", null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user