Handle 428 rate limiting.

This commit is contained in:
Greyson Parrelli
2021-05-05 12:49:18 -04:00
parent 02d060ca0a
commit 31e1c6f7aa
60 changed files with 1235 additions and 57 deletions

View File

@@ -89,6 +89,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
public abstract @Nullable ViewOnceExpirationInfo getNearestExpiringViewOnceMessage();
public abstract boolean isSent(long messageId);
public abstract List<MessageRecord> getProfileChangeDetailsRecords(long threadId, long afterTimestamp);
public abstract Set<Long> getAllRateLimitedMessageIds();
public abstract void markExpireStarted(long messageId);
public abstract void markExpireStarted(long messageId, long startTime);
@@ -101,6 +102,8 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
public abstract void markAsInsecure(long id);
public abstract void markAsPush(long id);
public abstract void markAsForcedSms(long id);
public abstract void markAsRateLimited(long id);
public abstract void clearRateLimitStatus(Collection<Long> ids);
public abstract void markAsDecryptFailed(long id);
public abstract void markAsDecryptDuplicate(long id);
public abstract void markAsNoSession(long id);

View File

@@ -785,6 +785,30 @@ public class MmsDatabase extends MessageDatabase {
notifyConversationListeners(threadId);
}
@Override
public void markAsRateLimited(long messageId) {
long threadId = getThreadIdForMessage(messageId);
updateMailboxBitmask(messageId, 0, Types.MESSAGE_RATE_LIMITED_BIT, Optional.of(threadId));
notifyConversationListeners(threadId);
}
@Override
public void clearRateLimitStatus(@NonNull Collection<Long> ids) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.beginTransaction();
try {
for (long id : ids) {
long threadId = getThreadIdForMessage(id);
updateMailboxBitmask(id, Types.MESSAGE_RATE_LIMITED_BIT, 0, Optional.of(threadId));
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
@Override
public void markAsPendingInsecureSmsFallback(long messageId) {
long threadId = getThreadIdForMessage(messageId);
@@ -1708,6 +1732,22 @@ public class MmsDatabase extends MessageDatabase {
throw new UnsupportedOperationException();
}
@Override
public Set<Long> getAllRateLimitedMessageIds() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = "(" + MESSAGE_BOX + " & " + Types.TOTAL_MASK + " & " + Types.MESSAGE_RATE_LIMITED_BIT + ") > 0";
Set<Long> ids = new HashSet<>();
try (Cursor cursor = db.query(TABLE_NAME, new String[] { ID }, where, null, null, null, null)) {
while (cursor.moveToNext()) {
ids.add(CursorUtil.requireLong(cursor, ID));
}
}
return ids;
}
@Override
void deleteThreads(@NonNull Set<Long> threadIds) {
Log.d(TAG, "deleteThreads(count: " + threadIds.size() + ")");

View File

@@ -28,7 +28,34 @@ public interface MmsSmsColumns {
public static final String REACTIONS_LAST_SEEN = "reactions_last_seen";
public static final String REMOTE_DELETED = "remote_deleted";
/**
* For storage efficiency, all types are stored within a single 64-bit integer column in the
* database. There are various areas reserved for different classes of data.
*
* When carving out a new area, if it's storing a bunch of mutually-exclusive flags (like in
* {@link #BASE_TYPE_MASK}, you should store integers in that area. If multiple flags can be set
* within a category, you'll have to store them as bits. Just keep in mind that storing as bits
* means we can store less data (i.e. 4 bits can store 16 exclusive values, or 4 non-exclusive
* values). This was not always followed in the past, and now we've wasted some space.
*
* Note: We technically could use up to 64 bits, but {@link #TOTAL_MASK} is currently just set to
* look at 32. Theoretically if we needed more bits, we could just use them and expand the size of
* {@link #TOTAL_MASK}.
*
* <pre>
* _____________________________________ ENCRYPTION ({@link #ENCRYPTION_MASK})
* | _____________________________ SECURE MESSAGE INFORMATION (no mask, but look at {@link #SECURE_MESSAGE_BIT})
* | | ________________________ GROUPS (no mask, but look at {@link #GROUP_UPDATE_BIT})
* | | | _________________ KEY_EXCHANGE ({@link #KEY_EXCHANGE_MASK})
* | | | | _________ MESSAGE_ATTRIBUTES ({@link #MESSAGE_ATTRIBUTE_MASK})
* | | | | | ____ BASE_TYPE ({@link #BASE_TYPE_MASK})
* ___|___ _| _| ___|__ | __|_
* | | | | | | | | | || |
* 0000 0000 0000 0000 0000 0000 0000 0000
* </pre>
*/
public static class Types {
protected static final long TOTAL_MASK = 0xFFFFFFFF;
// Base Types
@@ -63,8 +90,10 @@ public interface MmsSmsColumns {
OUTGOING_AUDIO_CALL_TYPE, OUTGOING_VIDEO_CALL_TYPE};
// Message attributes
protected static final long MESSAGE_ATTRIBUTE_MASK = 0xE0;
protected static final long MESSAGE_FORCE_SMS_BIT = 0x40;
protected static final long MESSAGE_ATTRIBUTE_MASK = 0xE0;
protected static final long MESSAGE_RATE_LIMITED_BIT = 0x80;
protected static final long MESSAGE_FORCE_SMS_BIT = 0x40;
// Note: Might be wise to reserve 0x20 -- it would let us expand BASE_MASK by a bit if needed
// Key Exchange Information
protected static final long KEY_EXCHANGE_MASK = 0xFF00;
@@ -210,6 +239,10 @@ public interface MmsSmsColumns {
return (type & KEY_EXCHANGE_IDENTITY_UPDATE_BIT) != 0;
}
public static boolean isRateLimited(long type) {
return (type & MESSAGE_RATE_LIMITED_BIT) != 0;
}
public static boolean isCallLog(long type) {
return isIncomingAudioCall(type) ||
isIncomingVideoCall(type) ||

View File

@@ -47,7 +47,6 @@ import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo;
@@ -321,6 +320,27 @@ public class SmsDatabase extends MessageDatabase {
updateTypeBitmask(id, Types.PUSH_MESSAGE_BIT, Types.MESSAGE_FORCE_SMS_BIT);
}
@Override
public void markAsRateLimited(long id) {
updateTypeBitmask(id, 0, Types.MESSAGE_RATE_LIMITED_BIT);
}
@Override
public void clearRateLimitStatus(@NonNull Collection<Long> ids) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.beginTransaction();
try {
for (long id : ids) {
updateTypeBitmask(id, Types.MESSAGE_RATE_LIMITED_BIT, 0);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
@Override
public void markAsDecryptFailed(long id) {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_FAILED_BIT);
@@ -887,6 +907,22 @@ public class SmsDatabase extends MessageDatabase {
return new Pair<>(messageId, threadId);
}
@Override
public Set<Long> getAllRateLimitedMessageIds() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = "(" + TYPE + " & " + Types.TOTAL_MASK + " & " + Types.MESSAGE_RATE_LIMITED_BIT + ") > 0";
Set<Long> ids = new HashSet<>();
try (Cursor cursor = db.query(TABLE_NAME, new String[] { ID }, where, null, null, null, null)) {
while (cursor.moveToNext()) {
ids.add(CursorUtil.requireLong(cursor, ID));
}
}
return ids;
}
@Override
public List<MessageRecord> getProfileChangeDetailsRecords(long threadId, long afterTimestamp) {
String where = THREAD_ID + " = ? AND " + DATE_RECEIVED + " >= ? AND " + TYPE + " = ?";

View File

@@ -423,6 +423,10 @@ public abstract class MessageRecord extends DisplayRecord {
return SmsDatabase.Types.isContentBundleKeyExchange(type);
}
public boolean isRateLimited() {
return SmsDatabase.Types.isRateLimited(type);
}
public boolean isIdentityUpdate() {
return SmsDatabase.Types.isIdentityUpdate(type);
}