Blur avatar photos from unknown senders when in message request state.

This commit is contained in:
Cody Henthorne
2021-04-23 14:42:51 -04:00
committed by GitHub
parent bf124b87fa
commit ad81b310e3
19 changed files with 546 additions and 113 deletions

View File

@@ -15,11 +15,9 @@ import com.google.protobuf.InvalidProtocolBufferException;
import org.signal.core.util.logging.Log;
import org.signal.storageservice.protos.groups.AccessControl;
import org.signal.storageservice.protos.groups.GroupChange;
import org.signal.storageservice.protos.groups.Member;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.protos.groups.local.DecryptedMember;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.groups.GroupMasterKey;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
@@ -447,15 +445,17 @@ public final class GroupDatabase extends Database {
contentValues.put(MMS, groupId.isMms());
List<RecipientId> groupMembers = members;
if (groupMasterKey != null) {
if (groupState == null) {
throw new AssertionError("V2 master key but no group state");
}
groupId.requireV2();
groupMembers = getV2GroupMembers(groupState);
contentValues.put(V2_MASTER_KEY, groupMasterKey.serialize());
contentValues.put(V2_REVISION, groupState.getRevision());
contentValues.put(V2_DECRYPTED_GROUP, groupState.toByteArray());
contentValues.put(MEMBERS, serializeV2GroupMembers(groupState));
contentValues.put(MEMBERS, RecipientId.toSerializedList(groupMembers));
} else {
if (groupId.isV2()) {
throw new AssertionError("V2 group id but no master key");
@@ -468,6 +468,10 @@ public final class GroupDatabase extends Database {
recipientDatabase.setExpireMessages(groupRecipientId, groupState.getDisappearingMessagesTimer().getDuration());
}
if (groupMembers != null && (groupId.isMms() || Recipient.resolved(groupRecipientId).isProfileSharing())) {
recipientDatabase.setHasGroupsInCommon(groupMembers);
}
Recipient.live(groupRecipientId).refresh();
notifyConversationListListeners();
@@ -585,10 +589,11 @@ public final class GroupDatabase extends Database {
contentValues.put(UNMIGRATED_V1_MEMBERS, unmigratedV1Members.isEmpty() ? null : RecipientId.toSerializedList(unmigratedV1Members));
}
List<RecipientId> groupMembers = getV2GroupMembers(decryptedGroup);
contentValues.put(TITLE, title);
contentValues.put(V2_REVISION, decryptedGroup.getRevision());
contentValues.put(V2_DECRYPTED_GROUP, decryptedGroup.toByteArray());
contentValues.put(MEMBERS, serializeV2GroupMembers(decryptedGroup));
contentValues.put(MEMBERS, RecipientId.toSerializedList(groupMembers));
contentValues.put(ACTIVE, gv2GroupActive(decryptedGroup) ? 1 : 0);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,
@@ -599,6 +604,10 @@ public final class GroupDatabase extends Database {
recipientDatabase.setExpireMessages(groupRecipientId, decryptedGroup.getDisappearingMessagesTimer().getDuration());
}
if (groupMembers != null && (groupId.isMms() || Recipient.resolved(groupRecipientId).isProfileSharing())) {
recipientDatabase.setHasGroupsInCommon(groupMembers);
}
Recipient.live(groupRecipientId).refresh();
notifyConversationListListeners();
@@ -742,11 +751,11 @@ public final class GroupDatabase extends Database {
return groupMembers;
}
private static String serializeV2GroupMembers(@NonNull DecryptedGroup decryptedGroup) {
private static List<RecipientId> getV2GroupMembers(@NonNull DecryptedGroup decryptedGroup) {
List<UUID> uuids = DecryptedGroupUtil.membersToUuidList(decryptedGroup.getMembersList());
List<RecipientId> recipientIds = uuidsToRecipientIds(uuids);
return RecipientId.toSerializedList(recipientIds);
return recipientIds;
}
public @NonNull List<GroupId.V2> getAllGroupV2Ids() {

View File

@@ -8,6 +8,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.arch.core.util.Function;
import com.annimon.stream.Stream;
import com.google.protobuf.ByteString;
@@ -31,6 +32,7 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime;
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileKeyCredentialColumnData;
import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras;
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
@@ -43,8 +45,8 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.storage.StorageRecordUpdate;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.storage.StorageSyncModels;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Bitmask;
@@ -68,8 +70,6 @@ import org.whispersystems.signalservice.api.storage.SignalAccountRecord;
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
import org.whispersystems.signalservice.api.storage.SignalGroupV2Record;
import org.whispersystems.signalservice.api.storage.SignalRecord;
import org.whispersystems.signalservice.api.storage.SignalStorageRecord;
import org.whispersystems.signalservice.api.storage.StorageId;
import org.whispersystems.signalservice.api.util.UuidUtil;
@@ -142,8 +142,10 @@ public class RecipientDatabase extends Database {
private static final String LAST_SESSION_RESET = "last_session_reset";
private static final String WALLPAPER = "wallpaper";
private static final String WALLPAPER_URI = "wallpaper_file";
public static final String ABOUT = "about";
public static final String ABOUT_EMOJI = "about_emoji";
public static final String ABOUT = "about";
public static final String ABOUT_EMOJI = "about_emoji";
private static final String EXTRAS = "extras";
private static final String GROUPS_IN_COMMON = "groups_in_common";
public static final String SEARCH_PROFILE_NAME = "search_signal_profile";
private static final String SORT_NAME = "sort_name";
@@ -170,12 +172,13 @@ public class RecipientDatabase extends Database {
STORAGE_SERVICE_ID, DIRTY,
MENTION_SETTING, WALLPAPER, WALLPAPER_URI,
MENTION_SETTING,
ABOUT, ABOUT_EMOJI
ABOUT, ABOUT_EMOJI,
EXTRAS, GROUPS_IN_COMMON
};
private static final String[] ID_PROJECTION = new String[]{ID};
private static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, "COALESCE(" + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ") AS " + SEARCH_PROFILE_NAME, "COALESCE(" + nullIfEmpty(SYSTEM_JOINED_NAME) + ", " + nullIfEmpty(SYSTEM_GIVEN_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ") AS " + SORT_NAME};
public static final String[] SEARCH_PROJECTION_NAMES = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, SEARCH_PROFILE_NAME, SORT_NAME};
private static final String[] SEARCH_PROJECTION = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, EXTRAS, GROUPS_IN_COMMON, "COALESCE(" + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ") AS " + SEARCH_PROFILE_NAME, "COALESCE(" + nullIfEmpty(SYSTEM_JOINED_NAME) + ", " + nullIfEmpty(SYSTEM_GIVEN_NAME) + ", " + nullIfEmpty(PROFILE_JOINED_NAME) + ", " + nullIfEmpty(PROFILE_GIVEN_NAME) + ", " + nullIfEmpty(USERNAME) + ") AS " + SORT_NAME};
public static final String[] SEARCH_PROJECTION_NAMES = new String[]{ID, SYSTEM_JOINED_NAME, PHONE, EMAIL, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, REGISTERED, ABOUT, ABOUT_EMOJI, EXTRAS, GROUPS_IN_COMMON, SEARCH_PROFILE_NAME, SORT_NAME};
private static final String[] TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
.map(columnName -> TABLE_NAME + "." + columnName)
.toList().toArray(new String[0]);
@@ -371,7 +374,9 @@ public class RecipientDatabase extends Database {
WALLPAPER + " BLOB DEFAULT NULL, " +
WALLPAPER_URI + " TEXT DEFAULT NULL, " +
ABOUT + " TEXT DEFAULT NULL, " +
ABOUT_EMOJI + " TEXT DEFAULT NULL);";
ABOUT_EMOJI + " TEXT DEFAULT NULL, " +
EXTRAS + " BLOB DEFAULT NULL, " +
GROUPS_IN_COMMON + " INTEGER DEFAULT 0);";
private static final String INSIGHTS_INVITEE_LIST = "SELECT " + TABLE_NAME + "." + ID +
" FROM " + TABLE_NAME +
@@ -1461,6 +1466,7 @@ public class RecipientDatabase extends Database {
byte[] wallpaper = CursorUtil.requireBlob(cursor, WALLPAPER);
String about = CursorUtil.requireString(cursor, ABOUT);
String aboutEmoji = CursorUtil.requireString(cursor, ABOUT_EMOJI);
boolean hasGroupsInCommon = CursorUtil.requireBoolean(cursor, GROUPS_IN_COMMON);
MaterialColor color;
byte[] profileKey = null;
@@ -1549,7 +1555,9 @@ public class RecipientDatabase extends Database {
chatWallpaper,
about,
aboutEmoji,
getSyncExtras(cursor));
getSyncExtras(cursor),
getExtras(cursor),
hasGroupsInCommon);
}
private static @NonNull RecipientSettings.SyncExtras getSyncExtras(@NonNull Cursor cursor) {
@@ -1565,6 +1573,23 @@ public class RecipientDatabase extends Database {
return new RecipientSettings.SyncExtras(storageProto, groupMasterKey, identityKey, identityStatus, archived, forcedUnread);
}
private static @Nullable Recipient.Extras getExtras(@NonNull Cursor cursor) {
return Recipient.Extras.from(getRecipientExtras(cursor));
}
private static @Nullable RecipientExtras getRecipientExtras(@NonNull Cursor cursor) {
final Optional<byte[]> blob = CursorUtil.getBlob(cursor, EXTRAS);
return blob.transform(b -> {
try {
return RecipientExtras.parseFrom(b);
} catch (InvalidProtocolBufferException e) {
Log.w(TAG, e);
throw new AssertionError(e);
}
}).orNull();
}
public BulkOperationsHandle beginBulkSystemContactUpdate() {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.beginTransaction();
@@ -1986,6 +2011,14 @@ public class RecipientDatabase extends Database {
boolean profiledUpdated = update(id, contentValues);
boolean colorUpdated = enabled && setColorIfNotSetInternal(id, ContactColors.generateFor(Recipient.resolved(id).getDisplayName(context)));
if (profiledUpdated && enabled) {
Optional<GroupDatabase.GroupRecord> group = DatabaseFactory.getGroupDatabase(context).getGroup(id);
if (group.isPresent()) {
setHasGroupsInCommon(group.get().getMembers());
}
}
if (profiledUpdated || colorUpdated) {
markDirty(id, DirtyState.UPDATE);
Recipient.live(id).refresh();
@@ -2841,6 +2874,93 @@ public class RecipientDatabase extends Database {
}
}
public void markPreMessageRequestRecipientsAsProfileSharingEnabled(long messageRequestEnableTime) {
String[] whereArgs = SqlUtil.buildArgs(messageRequestEnableTime, messageRequestEnableTime);
String select = "SELECT r." + ID + " FROM " + TABLE_NAME + " AS r "
+ "INNER JOIN " + ThreadDatabase.TABLE_NAME + " AS t ON t." + ThreadDatabase.RECIPIENT_ID + " = r." + ID + " WHERE "
+ "r." + PROFILE_SHARING + " = 0 AND "
+ "("
+ "EXISTS(SELECT 1 FROM " + SmsDatabase.TABLE_NAME + " WHERE " + SmsDatabase.THREAD_ID + " = t." + ThreadDatabase.ID + " AND " + SmsDatabase.DATE_RECEIVED + " < ?) "
+ "OR "
+ "EXISTS(SELECT 1 FROM " + MmsDatabase.TABLE_NAME + " WHERE " + MmsDatabase.THREAD_ID + " = t." + ThreadDatabase.ID + " AND " + MmsDatabase.DATE_RECEIVED + " < ?) "
+ ")";
List<Long> idsToUpdate = new ArrayList<>();
try (Cursor cursor = databaseHelper.getReadableDatabase().rawQuery(select, whereArgs)) {
while (cursor.moveToNext()) {
idsToUpdate.add(CursorUtil.requireLong(cursor, ID));
}
}
if (Util.hasItems(idsToUpdate)) {
SqlUtil.Query query = SqlUtil.buildCollectionQuery(ID, idsToUpdate);
ContentValues values = new ContentValues(1);
values.put(PROFILE_SHARING, 1);
databaseHelper.getWritableDatabase().update(TABLE_NAME, values, query.getWhere(), query.getWhereArgs());
for (long id : idsToUpdate) {
Recipient.live(RecipientId.from(id)).refresh();
}
}
}
public void setHasGroupsInCommon(@NonNull List<RecipientId> recipientIds) {
SqlUtil.Query query = SqlUtil.buildCollectionQuery(ID, recipientIds);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
try (Cursor cursor = db.query(TABLE_NAME,
new String[]{ID},
query.getWhere() + " AND " + GROUPS_IN_COMMON + " = 0",
query.getWhereArgs(),
null,
null,
null))
{
List<Long> idsToUpdate = new ArrayList<>(cursor.getCount());
while (cursor.moveToNext()) {
idsToUpdate.add(CursorUtil.requireLong(cursor, ID));
}
if (Util.hasItems(idsToUpdate)) {
query = SqlUtil.buildCollectionQuery(ID, idsToUpdate);
ContentValues values = new ContentValues();
values.put(GROUPS_IN_COMMON, 1);
int count = db.update(TABLE_NAME, values, query.getWhere(), query.getWhereArgs());
if (count > 0) {
for (long id : idsToUpdate) {
Recipient.live(RecipientId.from(id)).refresh();
}
}
}
}
}
public void manuallyShowAvatar(@NonNull RecipientId recipientId) {
updateExtras(recipientId, b -> b.setManuallyShownAvatar(true));
}
private void updateExtras(@NonNull RecipientId recipientId, @NonNull Function<RecipientExtras.Builder, RecipientExtras.Builder> updater) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.beginTransaction();
try {
try (Cursor cursor = db.query(TABLE_NAME, new String[]{ID, EXTRAS}, ID_WHERE, SqlUtil.buildArgs(recipientId), null, null, null)) {
if (cursor.moveToNext()) {
RecipientExtras state = getRecipientExtras(cursor);
RecipientExtras.Builder builder = state != null ? state.toBuilder() : RecipientExtras.newBuilder();
byte[] updatedState = updater.apply(builder).build().toByteArray();
ContentValues values = new ContentValues(1);
values.put(EXTRAS, updatedState);
db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(CursorUtil.requireLong(cursor, ID)));
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
Recipient.live(recipientId).refresh();
}
void markDirty(@NonNull RecipientId recipientId, @NonNull DirtyState dirtyState) {
Log.d(TAG, "Attempting to mark " + recipientId + " with dirty state " + dirtyState);
@@ -3232,7 +3352,9 @@ public class RecipientDatabase extends Database {
private final ChatWallpaper wallpaper;
private final String about;
private final String aboutEmoji;
private final SyncExtras syncExtras;
private final SyncExtras syncExtras;
private final Recipient.Extras extras;
private final boolean hasGroupsInCommon;
RecipientSettings(@NonNull RecipientId id,
@Nullable UUID uuid,
@@ -3273,7 +3395,9 @@ public class RecipientDatabase extends Database {
@Nullable ChatWallpaper wallpaper,
@Nullable String about,
@Nullable String aboutEmoji,
@NonNull SyncExtras syncExtras)
@NonNull SyncExtras syncExtras,
@Nullable Recipient.Extras extras,
boolean hasGroupsInCommon)
{
this.id = id;
this.uuid = uuid;
@@ -3316,7 +3440,9 @@ public class RecipientDatabase extends Database {
this.wallpaper = wallpaper;
this.about = about;
this.aboutEmoji = aboutEmoji;
this.syncExtras = syncExtras;
this.syncExtras = syncExtras;
this.extras = extras;
this.hasGroupsInCommon = hasGroupsInCommon;
}
public RecipientId getId() {
@@ -3483,6 +3609,14 @@ public class RecipientDatabase extends Database {
return syncExtras;
}
public @Nullable Recipient.Extras getExtras() {
return extras;
}
public boolean hasGroupsInCommon() {
return hasGroupsInCommon;
}
long getCapabilities() {
return capabilities;
}

View File

@@ -43,8 +43,8 @@ import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.SqlCipherDatabaseHook;
import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
@@ -172,8 +172,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
private static final int PAYMENTS = 91;
private static final int CLEAN_STORAGE_IDS = 92;
private static final int MP4_GIF_SUPPORT = 93;
private static final int BLUR_AVATARS = 94;
private static final int DATABASE_VERSION = 93;
private static final int DATABASE_VERSION = 94;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@@ -1304,6 +1305,23 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
db.execSQL("ALTER TABLE part ADD COLUMN video_gif INTEGER DEFAULT 0");
}
if (oldVersion < BLUR_AVATARS) {
db.execSQL("ALTER TABLE recipient ADD COLUMN extras BLOB DEFAULT NULL");
db.execSQL("ALTER TABLE recipient ADD COLUMN groups_in_common INTEGER DEFAULT 0");
String secureOutgoingSms = "EXISTS(SELECT 1 FROM sms WHERE thread_id = t._id AND (type & 31) = 23 AND (type & 10485760) AND (type & 131072 = 0))";
String secureOutgoingMms = "EXISTS(SELECT 1 FROM mms WHERE thread_id = t._id AND (msg_box & 31) = 23 AND (msg_box & 10485760) AND (msg_box & 131072 = 0))";
String selectIdsToUpdateProfileSharing = "SELECT r._id FROM recipient AS r INNER JOIN thread AS t ON r._id = t.recipient_ids WHERE profile_sharing = 0 AND (" + secureOutgoingSms + " OR " + secureOutgoingMms + ")";
db.rawExecSQL("UPDATE recipient SET profile_sharing = 1 WHERE _id IN (" + selectIdsToUpdateProfileSharing + ")");
String selectIdsWithGroupsInCommon = "SELECT r._id FROM recipient AS r WHERE EXISTS("
+ "SELECT 1 FROM groups AS g INNER JOIN recipient AS gr ON (g.recipient_id = gr._id AND gr.profile_sharing = 1) WHERE g.active = 1 AND (g.members LIKE r._id || ',%' OR g.members LIKE '%,' || r._id || ',%' OR g.members LIKE '%,' || r._id)"
+ ")";
db.rawExecSQL("UPDATE recipient SET groups_in_common = 1 WHERE _id IN (" + selectIdsWithGroupsInCommon + ")");
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();