That fuck shit the fascists are using
1package org.tm.archive.database
2
3import android.content.ContentValues
4import android.content.Context
5import androidx.core.content.contentValuesOf
6import org.signal.core.util.SqlUtil
7import org.signal.core.util.delete
8import org.signal.core.util.deleteAll
9import org.signal.core.util.logging.Log
10import org.signal.core.util.requireNonNullString
11import org.signal.core.util.select
12import org.signal.core.util.withinTransaction
13
14/**
15 * Keeps track of the numbers we've previously queried CDS for.
16 *
17 * This is important for rate-limiting: our rate-limiting strategy hinges on keeping
18 * an accurate history of numbers we've queried so that we're only "charged" for
19 * querying new numbers.
20 */
21class CdsTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper) {
22 companion object {
23 private val TAG = Log.tag(CdsTable::class.java)
24
25 const val TABLE_NAME = "cds"
26
27 private const val ID = "_id"
28 const val E164 = "e164"
29 private const val LAST_SEEN_AT = "last_seen_at"
30
31 const val CREATE_TABLE = """
32 CREATE TABLE $TABLE_NAME (
33 $ID INTEGER PRIMARY KEY,
34 $E164 TEXT NOT NULL UNIQUE ON CONFLICT IGNORE,
35 $LAST_SEEN_AT INTEGER DEFAULT 0
36 )
37 """
38 }
39
40 fun getAllE164s(): Set<String> {
41 val e164s: MutableSet<String> = mutableSetOf()
42
43 readableDatabase
44 .select(E164)
45 .from(TABLE_NAME)
46 .run()
47 .use { cursor ->
48 while (cursor.moveToNext()) {
49 e164s += cursor.requireNonNullString(E164)
50 }
51 }
52
53 return e164s
54 }
55
56 /**
57 * Saves the set of e164s used after a full refresh.
58 * @param fullE164s All of the e164s used in the last CDS query (previous and new).
59 * @param seenE164s The E164s that were seen in either the system contacts or recipients table. This is different from [fullE164s] in that [fullE164s]
60 * includes every number we've ever seen, even if it's not in our contacts anymore.
61 */
62 fun updateAfterFullCdsQuery(fullE164s: Set<String>, seenE164s: Set<String>) {
63 val lastSeen = System.currentTimeMillis()
64
65 writableDatabase.withinTransaction { db ->
66 val existingE164s: Set<String> = getAllE164s()
67 val removedE164s: Set<String> = existingE164s - fullE164s
68 val addedE164s: Set<String> = fullE164s - existingE164s
69
70 if (removedE164s.isNotEmpty()) {
71 SqlUtil.buildCollectionQuery(E164, removedE164s)
72 .forEach { db.delete(TABLE_NAME, it.where, it.whereArgs) }
73 }
74
75 if (addedE164s.isNotEmpty()) {
76 val insertValues: List<ContentValues> = addedE164s.map { contentValuesOf(E164 to it) }
77
78 SqlUtil.buildBulkInsert(TABLE_NAME, arrayOf(E164), insertValues)
79 .forEach { db.execSQL(it.where, it.whereArgs) }
80 }
81
82 if (seenE164s.isNotEmpty()) {
83 val contentValues = contentValuesOf(LAST_SEEN_AT to lastSeen)
84
85 SqlUtil.buildCollectionQuery(E164, seenE164s)
86 .forEach { query -> db.update(TABLE_NAME, contentValues, query.where, query.whereArgs) }
87 }
88 }
89 }
90
91 /**
92 * Updates after a partial CDS query. Will not insert new entries. Instead, this will simply update the lastSeen timestamp of any entry we already have.
93 * @param seenE164s The newly-added E164s that we hadn't previously queried for.
94 */
95 fun updateAfterPartialCdsQuery(seenE164s: Set<String>) {
96 val lastSeen = System.currentTimeMillis()
97
98 writableDatabase.withinTransaction { db ->
99 val contentValues = contentValuesOf(LAST_SEEN_AT to lastSeen)
100
101 SqlUtil.buildCollectionQuery(E164, seenE164s)
102 .forEach { query -> db.update(TABLE_NAME, contentValues, query.where, query.whereArgs) }
103 }
104 }
105
106 /**
107 * Wipes the entire table.
108 */
109 fun clearAll() {
110 writableDatabase.deleteAll(TABLE_NAME)
111 }
112}