Cleanup bad recipients.
parent
89e075c56e
commit
95333eccd4
|
@ -0,0 +1,92 @@
|
|||
package org.thoughtcrime.securesms.database.helpers;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.DelimiterUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class RecipientIdCleanupHelper {
|
||||
|
||||
private static final String TAG = Log.tag(RecipientIdCleanupHelper.class);
|
||||
|
||||
public static void execute(@NonNull SQLiteDatabase db) {
|
||||
Log.i(TAG, "Beginning migration.");
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
Pattern pattern = Pattern.compile("^[0-9\\-+]+$");
|
||||
Set<String> deletionCandidates = new HashSet<>();
|
||||
|
||||
try (Cursor cursor = db.query("recipient", new String[] { "_id", "phone" }, "group_id IS NULL AND email IS NULL", null, null, null, null)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
String id = cursor.getString(cursor.getColumnIndexOrThrow("_id"));
|
||||
String phone = cursor.getString(cursor.getColumnIndexOrThrow("phone"));
|
||||
|
||||
if (TextUtils.isEmpty(phone) || !pattern.matcher(phone).matches()) {
|
||||
Log.i(TAG, "Recipient ID " + id + " has non-numeric characters and can potentially be deleted.");
|
||||
|
||||
if (!isIdUsed(db, "identities", "address", id) &&
|
||||
!isIdUsed(db, "sessions", "address", id) &&
|
||||
!isIdUsed(db, "thread", "recipient_ids", id) &&
|
||||
!isIdUsed(db, "sms", "address", id) &&
|
||||
!isIdUsed(db, "mms", "address", id) &&
|
||||
!isIdUsed(db, "mms", "quote_author", id) &&
|
||||
!isIdUsed(db, "group_receipts", "address", id) &&
|
||||
!isIdUsed(db, "groups", "recipient_id", id))
|
||||
{
|
||||
Log.i(TAG, "Determined ID " + id + " is unused in non-group membership. Marking for potential deletion.");
|
||||
deletionCandidates.add(id);
|
||||
} else {
|
||||
Log.i(TAG, "Found that ID " + id + " is actually used in another table.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> deletions = findUnusedInGroupMembership(db, deletionCandidates);
|
||||
|
||||
for (String deletion : deletions) {
|
||||
Log.i(TAG, "Deleting ID " + deletion);
|
||||
db.delete("recipient", "_id = ?", new String[] { String.valueOf(deletion) });
|
||||
}
|
||||
|
||||
Log.i(TAG, "Migration took " + (System.currentTimeMillis() - startTime) + " ms.");
|
||||
}
|
||||
|
||||
private static boolean isIdUsed(@NonNull SQLiteDatabase db, @NonNull String tableName, @NonNull String columnName, String id) {
|
||||
try (Cursor cursor = db.query(tableName, new String[] { columnName }, columnName + " = ?", new String[] { id }, null, null, null, "1")) {
|
||||
boolean used = cursor != null && cursor.moveToFirst();
|
||||
if (used) {
|
||||
Log.i(TAG, "Recipient " + id + " was used in (" + tableName + ", " + columnName + ")");
|
||||
}
|
||||
return used;
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> findUnusedInGroupMembership(@NonNull SQLiteDatabase db, Set<String> candidates) {
|
||||
Set<String> unused = new HashSet<>(candidates);
|
||||
|
||||
try (Cursor cursor = db.rawQuery("SELECT members FROM groups", null)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow("members"));
|
||||
String[] members = DelimiterUtil.split(serializedMembers, ',');
|
||||
|
||||
for (String member : members) {
|
||||
if (unused.remove(member)) {
|
||||
Log.i(TAG, "Recipient " + member + " was found in a group membership list.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unused;
|
||||
}
|
||||
}
|
|
@ -39,13 +39,9 @@ import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
|||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.DelimiterUtil;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
@ -78,8 +74,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||
private static final int VIEW_ONCE_ONLY = 23;
|
||||
private static final int RECIPIENT_IDS = 24;
|
||||
private static final int RECIPIENT_SEARCH = 25;
|
||||
private static final int RECIPIENT_CLEANUP = 26;
|
||||
|
||||
private static final int DATABASE_VERSION = 25;
|
||||
private static final int DATABASE_VERSION = 26;
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
||||
private final Context context;
|
||||
|
@ -513,6 +510,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||
}
|
||||
}
|
||||
|
||||
if (oldVersion < RECIPIENT_CLEANUP) {
|
||||
RecipientIdCleanupHelper.execute(db);
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
|
|
|
@ -38,12 +38,13 @@ public class ApplicationMigrations {
|
|||
|
||||
private static final int LEGACY_CANONICAL_VERSION = 455;
|
||||
|
||||
public static final int CURRENT_VERSION = 3;
|
||||
public static final int CURRENT_VERSION = 4;
|
||||
|
||||
private static final class Version {
|
||||
static final int LEGACY = 1;
|
||||
static final int RECIPIENT_ID = 2;
|
||||
static final int RECIPIENT_SEARCH = 3;
|
||||
static final int LEGACY = 1;
|
||||
static final int RECIPIENT_ID = 2;
|
||||
static final int RECIPIENT_SEARCH = 3;
|
||||
static final int RECIPIENT_CLEANUP = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -168,6 +169,10 @@ public class ApplicationMigrations {
|
|||
jobs.put(Version.RECIPIENT_SEARCH, new RecipientSearchMigrationJob());
|
||||
}
|
||||
|
||||
if (lastSeenVersion < Version.RECIPIENT_CLEANUP) {
|
||||
jobs.put(Version.RECIPIENT_CLEANUP, new DatabaseMigrationJob());
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue