That fuck shit the fascists are using
at master 216 lines 6.5 kB view raw
1package org.tm.archive.database 2 3import app.cash.exhaustive.Exhaustive 4import org.tm.archive.database.model.RecipientRecord 5import org.tm.archive.recipients.RecipientId 6import org.whispersystems.signalservice.api.push.ServiceId 7import org.whispersystems.signalservice.api.push.ServiceId.ACI 8import org.whispersystems.signalservice.api.push.ServiceId.PNI 9 10/** 11 * Encapsulates data around processing a tuple of user data into a user entry in [RecipientTable]. 12 * Also lets you apply a list of [PnpOperation]s to get what the resulting dataset would be. 13 */ 14data class PnpDataSet( 15 val e164: String?, 16 val pni: PNI?, 17 val aci: ACI?, 18 val byE164: RecipientId?, 19 val byPni: RecipientId?, 20 val byAci: RecipientId?, 21 val e164Record: RecipientRecord? = null, 22 val pniRecord: RecipientRecord? = null, 23 val aciRecord: RecipientRecord? = null 24) { 25 26 /** 27 * @return The common id if all non-null ids are equal, or null if all are null or at least one non-null pair doesn't match. 28 */ 29 val commonId: RecipientId? = findCommonId(listOf(byE164, byPni, byAci)) 30 31 /** The ID that would be used to contact this user. */ 32 val serviceId: ServiceId? = aci ?: pni 33 34 fun MutableSet<RecipientRecord>.replace(recipientId: RecipientId, update: (RecipientRecord) -> RecipientRecord) { 35 val toUpdate = this.first { it.id == recipientId } 36 this.removeIf { it.id == toUpdate.id } 37 this += update(toUpdate) 38 } 39 40 /** 41 * Applies the set of operations and returns the resulting dataset. 42 * Important: This only occurs _in memory_. You must still apply the operations to disk to persist them. 43 */ 44 fun perform(operations: LinkedHashSet<PnpOperation>): PnpDataSet { 45 if (operations.isEmpty()) { 46 return this 47 } 48 49 val records: MutableSet<RecipientRecord> = listOfNotNull(e164Record, pniRecord, aciRecord).toMutableSet() 50 51 for (operation in operations) { 52 @Exhaustive 53 when (operation) { 54 is PnpOperation.RemoveE164 -> { 55 records.replace(operation.recipientId) { it.copy(e164 = null) } 56 } 57 is PnpOperation.RemovePni -> { 58 records.replace(operation.recipientId) { record -> 59 record.copy( 60 pni = null, 61 aci = record.aci 62 ) 63 } 64 } 65 is PnpOperation.SetAci -> { 66 records.replace(operation.recipientId) { it.copy(aci = operation.aci) } 67 } 68 is PnpOperation.SetE164 -> { 69 records.replace(operation.recipientId) { it.copy(e164 = operation.e164) } 70 } 71 is PnpOperation.SetPni -> { 72 records.replace(operation.recipientId) { record -> 73 record.copy( 74 pni = operation.pni 75 ) 76 } 77 } 78 is PnpOperation.Merge -> { 79 val primary: RecipientRecord = records.first { it.id == operation.primaryId } 80 val secondary: RecipientRecord = records.first { it.id == operation.secondaryId } 81 82 records.replace(primary.id) { _ -> 83 primary.copy( 84 e164 = primary.e164 ?: secondary.e164, 85 pni = primary.pni ?: secondary.pni, 86 aci = primary.aci ?: secondary.aci 87 ) 88 } 89 90 records.removeIf { it.id == secondary.id } 91 } 92 is PnpOperation.SessionSwitchoverInsert -> Unit 93 is PnpOperation.ChangeNumberInsert -> Unit 94 } 95 } 96 97 val newE164Record = if (e164 != null) records.firstOrNull { it.e164 == e164 } else null 98 val newPniRecord = if (pni != null) records.firstOrNull { it.pni == pni } else null 99 val newAciRecord = if (aci != null) records.firstOrNull { it.aci == aci } else null 100 101 return this.copy( 102 byE164 = newE164Record?.id, 103 byPni = newPniRecord?.id, 104 byAci = newAciRecord?.id, 105 e164Record = newE164Record, 106 pniRecord = newPniRecord, 107 aciRecord = newAciRecord 108 ) 109 } 110 111 companion object { 112 private fun findCommonId(ids: List<RecipientId?>): RecipientId? { 113 val nonNull = ids.filterNotNull() 114 115 return when { 116 nonNull.isEmpty() -> null 117 nonNull.all { it == nonNull[0] } -> nonNull[0] 118 else -> null 119 } 120 } 121 } 122} 123 124/** 125 * Represents a set of actions that need to be applied to incorporate a tuple of user data 126 * into [RecipientTable]. 127 */ 128data class PnpChangeSet( 129 val id: PnpIdResolver, 130 val operations: LinkedHashSet<PnpOperation> = linkedSetOf(), 131 val breadCrumbs: List<String> = emptyList() 132) { 133 // We want to exclude breadcrumbs from equality for testing purposes 134 override fun equals(other: Any?): Boolean { 135 if (this === other) return true 136 if (javaClass != other?.javaClass) return false 137 138 other as PnpChangeSet 139 140 if (id != other.id) return false 141 if (operations != other.operations) return false 142 143 return true 144 } 145 146 override fun hashCode(): Int { 147 var result = id.hashCode() 148 result = 31 * result + operations.hashCode() 149 return result 150 } 151} 152 153sealed class PnpIdResolver { 154 data class PnpNoopId( 155 val recipientId: RecipientId 156 ) : PnpIdResolver() 157 158 data class PnpInsert( 159 val e164: String?, 160 val pni: PNI?, 161 val aci: ACI? 162 ) : PnpIdResolver() 163} 164 165/** 166 * An operation that needs to be performed on the [RecipientTable] as part of merging in new user data. 167 * Lets us describe various situations as a series of operations, making code clearer and tests easier. 168 */ 169sealed class PnpOperation { 170 abstract val recipientId: RecipientId 171 172 data class RemoveE164( 173 override val recipientId: RecipientId 174 ) : PnpOperation() 175 176 data class RemovePni( 177 override val recipientId: RecipientId 178 ) : PnpOperation() 179 180 data class SetE164( 181 override val recipientId: RecipientId, 182 val e164: String 183 ) : PnpOperation() 184 185 data class SetPni( 186 override val recipientId: RecipientId, 187 val pni: PNI 188 ) : PnpOperation() 189 190 data class SetAci( 191 override val recipientId: RecipientId, 192 val aci: ACI 193 ) : PnpOperation() 194 195 /** 196 * Merge two rows into one. Prefer data in the primary row when there's conflicts. Delete the secondary row afterwards. 197 */ 198 data class Merge( 199 val primaryId: RecipientId, 200 val secondaryId: RecipientId 201 ) : PnpOperation() { 202 override val recipientId: RecipientId 203 get() = throw UnsupportedOperationException() 204 } 205 206 data class SessionSwitchoverInsert( 207 override val recipientId: RecipientId, 208 val e164: String? 209 ) : PnpOperation() 210 211 data class ChangeNumberInsert( 212 override val recipientId: RecipientId, 213 val oldE164: String, 214 val newE164: String 215 ) : PnpOperation() 216}