That fuck shit the fascists are using
1package org.tm.archive.database
2
3import android.content.Context
4import org.signal.core.util.CursorUtil
5import org.signal.core.util.SqlUtil
6import org.signal.core.util.logging.Log
7import org.signal.core.util.readToSet
8import org.signal.core.util.requireInt
9import org.signal.core.util.requireNonNullBlob
10import org.signal.core.util.requireNonNullString
11import org.signal.core.util.requireString
12import org.signal.core.util.select
13import org.signal.libsignal.protocol.SignalProtocolAddress
14import org.signal.libsignal.protocol.state.SessionRecord
15import org.whispersystems.signalservice.api.push.ServiceId
16import org.whispersystems.signalservice.api.push.ServiceId.PNI
17import org.whispersystems.signalservice.api.push.SignalServiceAddress
18import java.io.IOException
19import java.util.LinkedList
20
21class SessionTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper) {
22 companion object {
23 private val TAG = Log.tag(SessionTable::class.java)
24
25 const val TABLE_NAME = "sessions"
26 const val ID = "_id"
27 const val ACCOUNT_ID = "account_id"
28 const val ADDRESS = "address"
29 const val DEVICE = "device"
30 const val RECORD = "record"
31 const val CREATE_TABLE = """
32 CREATE TABLE $TABLE_NAME (
33 $ID INTEGER PRIMARY KEY AUTOINCREMENT,
34 $ACCOUNT_ID TEXT NOT NULL,
35 $ADDRESS TEXT NOT NULL,
36 $DEVICE INTEGER NOT NULL,
37 $RECORD BLOB NOT NULL,
38 UNIQUE($ACCOUNT_ID, $ADDRESS, $DEVICE)
39 )
40 """
41 }
42
43 fun store(serviceId: ServiceId, address: SignalProtocolAddress, record: SessionRecord) {
44 require(address.name[0] != '+') { "Cannot insert an e164 into this table!" }
45
46 writableDatabase.compileStatement("INSERT INTO $TABLE_NAME ($ACCOUNT_ID, $ADDRESS, $DEVICE, $RECORD) VALUES (?, ?, ?, ?) ON CONFLICT ($ACCOUNT_ID, $ADDRESS, $DEVICE) DO UPDATE SET $RECORD = excluded.$RECORD").use { statement ->
47 statement.apply {
48 bindString(1, serviceId.toString())
49 bindString(2, address.name)
50 bindLong(3, address.deviceId.toLong())
51 bindBlob(4, record.serialize())
52 execute()
53 }
54 }
55 }
56
57 fun load(serviceId: ServiceId, address: SignalProtocolAddress): SessionRecord? {
58 val projection = arrayOf(RECORD)
59 val selection = "$ACCOUNT_ID = ? AND $ADDRESS = ? AND $DEVICE = ?"
60 val args = SqlUtil.buildArgs(serviceId, address.name, address.deviceId)
61
62 readableDatabase.query(TABLE_NAME, projection, selection, args, null, null, null).use { cursor ->
63 if (cursor.moveToFirst()) {
64 try {
65 return SessionRecord(cursor.requireNonNullBlob(RECORD))
66 } catch (e: IOException) {
67 Log.w(TAG, e)
68 }
69 }
70 }
71
72 return null
73 }
74
75 fun load(serviceId: ServiceId, addresses: List<SignalProtocolAddress>): List<SessionRecord?> {
76 val projection = arrayOf(ADDRESS, DEVICE, RECORD)
77 val query = "$ACCOUNT_ID = ? AND $ADDRESS = ? AND $DEVICE = ?"
78 val args: MutableList<Array<String>> = ArrayList(addresses.size)
79 val sessions: HashMap<SignalProtocolAddress, SessionRecord?> = LinkedHashMap(addresses.size)
80
81 for (address in addresses) {
82 args.add(SqlUtil.buildArgs(serviceId, address.name, address.deviceId))
83 sessions[address] = null
84 }
85
86 for (combinedQuery in SqlUtil.buildCustomCollectionQuery(query, args)) {
87 readableDatabase.query(TABLE_NAME, projection, combinedQuery.where, combinedQuery.whereArgs, null, null, null).use { cursor ->
88 while (cursor.moveToNext()) {
89 val address = cursor.requireNonNullString(ADDRESS)
90 val device = cursor.requireInt(DEVICE)
91 try {
92 val record = SessionRecord(cursor.requireNonNullBlob(RECORD))
93 sessions[SignalProtocolAddress(address, device)] = record
94 } catch (e: IOException) {
95 Log.w(TAG, e)
96 }
97 }
98 }
99 }
100
101 return sessions.values.toList()
102 }
103
104 fun getAllFor(serviceId: ServiceId, addressName: String): List<SessionRow> {
105 val results: MutableList<SessionRow> = mutableListOf()
106
107 readableDatabase.query(TABLE_NAME, null, "$ACCOUNT_ID = ? AND $ADDRESS = ?", SqlUtil.buildArgs(serviceId, addressName), null, null, null).use { cursor ->
108 while (cursor.moveToNext()) {
109 try {
110 results.add(
111 SessionRow(
112 CursorUtil.requireString(cursor, ADDRESS),
113 CursorUtil.requireInt(cursor, DEVICE),
114 SessionRecord(CursorUtil.requireBlob(cursor, RECORD))
115 )
116 )
117 } catch (e: IOException) {
118 Log.w(TAG, e)
119 }
120 }
121 }
122 return results
123 }
124
125 fun getAllFor(serviceId: ServiceId, addressNames: List<String?>): List<SessionRow> {
126 val query: SqlUtil.Query = SqlUtil.buildSingleCollectionQuery(ADDRESS, addressNames)
127 val results: MutableList<SessionRow> = LinkedList()
128
129 val queryString = "$ACCOUNT_ID = ? AND (${query.where})"
130 val queryArgs: Array<String> = arrayOf(serviceId.toString()) + query.whereArgs
131
132 readableDatabase.query(TABLE_NAME, null, queryString, queryArgs, null, null, null).use { cursor ->
133 while (cursor.moveToNext()) {
134 try {
135 results.add(
136 SessionRow(
137 address = CursorUtil.requireString(cursor, ADDRESS),
138 deviceId = CursorUtil.requireInt(cursor, DEVICE),
139 record = SessionRecord(cursor.requireNonNullBlob(RECORD))
140 )
141 )
142 } catch (e: IOException) {
143 Log.w(TAG, e)
144 }
145 }
146 }
147 return results
148 }
149
150 fun getAll(serviceId: ServiceId): List<SessionRow> {
151 val results: MutableList<SessionRow> = mutableListOf()
152
153 readableDatabase.query(TABLE_NAME, null, "$ACCOUNT_ID = ?", SqlUtil.buildArgs(serviceId), null, null, null).use { cursor ->
154 while (cursor.moveToNext()) {
155 try {
156 results.add(
157 SessionRow(
158 address = cursor.requireNonNullString(ADDRESS),
159 deviceId = cursor.requireInt(DEVICE),
160 record = SessionRecord(cursor.requireNonNullBlob(RECORD))
161 )
162 )
163 } catch (e: IOException) {
164 Log.w(TAG, e)
165 }
166 }
167 }
168 return results
169 }
170
171 fun getSubDevices(serviceId: ServiceId, addressName: String): List<Int> {
172 val projection = arrayOf(DEVICE)
173 val selection = "$ACCOUNT_ID = ? AND $ADDRESS = ? AND $DEVICE != ?"
174 val args = SqlUtil.buildArgs(serviceId, addressName, SignalServiceAddress.DEFAULT_DEVICE_ID)
175
176 val results: MutableList<Int> = mutableListOf()
177
178 readableDatabase.query(TABLE_NAME, projection, selection, args, null, null, null).use { cursor ->
179 while (cursor.moveToNext()) {
180 results.add(cursor.requireInt(DEVICE))
181 }
182 }
183 return results
184 }
185
186 fun delete(serviceId: ServiceId, address: SignalProtocolAddress) {
187 writableDatabase.delete(TABLE_NAME, "$ACCOUNT_ID = ? AND $ADDRESS = ? AND $DEVICE = ?", SqlUtil.buildArgs(serviceId, address.name, address.deviceId))
188 }
189
190 fun deleteAllFor(serviceId: ServiceId, addressName: String) {
191 writableDatabase.delete(TABLE_NAME, "$ACCOUNT_ID = ? AND $ADDRESS = ?", SqlUtil.buildArgs(serviceId, addressName))
192 }
193
194 fun hasSessionFor(serviceId: ServiceId, addressName: String): Boolean {
195 val query = "$ACCOUNT_ID = ? AND $ADDRESS = ?"
196 val args = SqlUtil.buildArgs(serviceId, addressName)
197 readableDatabase.query(TABLE_NAME, arrayOf("1"), query, args, null, null, null, "1").use { cursor ->
198 return cursor.moveToFirst()
199 }
200 }
201
202 /**
203 * @return True if a session exists with this address for _any_ of your identities.
204 */
205 fun hasAnySessionFor(addressName: String): Boolean {
206 readableDatabase
207 .select("1")
208 .from(TABLE_NAME)
209 .where("$ADDRESS = ?", addressName)
210 .run()
211 .use { cursor ->
212 return cursor.moveToFirst()
213 }
214 }
215
216 /**
217 * Given a set of serviceIds, this will give you back a filtered set of those ids that have any session with any of your identities.
218 *
219 * This was created for getting more debug info for a specific issue.
220 */
221 fun findAllThatHaveAnySession(serviceIds: Set<PNI>): Set<PNI> {
222 val output: MutableSet<PNI> = mutableSetOf()
223
224 for (query in SqlUtil.buildCollectionQuery(ADDRESS, serviceIds.map { it.toString() })) {
225 output += readableDatabase
226 .select(ADDRESS)
227 .from(TABLE_NAME)
228 .where(query.where, query.whereArgs)
229 .run()
230 .readToSet { PNI.parseOrThrow(it.requireString(ADDRESS)) }
231 }
232
233 return output
234 }
235
236 class SessionRow(val address: String, val deviceId: Int, val record: SessionRecord)
237}