That fuck shit the fascists are using
at master 237 lines 8.6 kB view raw
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}