That fuck shit the fascists are using
at master 293 lines 9.7 kB view raw
1package org.tm.archive.testing 2 3import okio.ByteString 4import okio.ByteString.Companion.toByteString 5import org.tm.archive.groups.GroupId 6import org.tm.archive.messages.SignalServiceProtoUtil.buildWith 7import org.tm.archive.messages.TestMessage 8import org.tm.archive.recipients.Recipient 9import org.tm.archive.recipients.RecipientId 10import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata 11import org.whispersystems.signalservice.internal.push.AttachmentPointer 12import org.whispersystems.signalservice.internal.push.BodyRange 13import org.whispersystems.signalservice.internal.push.Content 14import org.whispersystems.signalservice.internal.push.DataMessage 15import org.whispersystems.signalservice.internal.push.EditMessage 16import org.whispersystems.signalservice.internal.push.Envelope 17import org.whispersystems.signalservice.internal.push.GroupContextV2 18import org.whispersystems.signalservice.internal.push.SyncMessage 19import java.util.UUID 20import kotlin.random.Random 21import kotlin.random.nextInt 22import kotlin.time.Duration.Companion.days 23 24/** 25 * Random but deterministic fuzzer for create various message content protos. 26 */ 27object MessageContentFuzzer { 28 29 private val mediaTypes = listOf("image/png", "image/jpeg", "image/heic", "image/heif", "image/avif", "image/webp", "image/gif", "audio/aac", "audio/*", "video/mp4", "video/*", "text/x-vcard", "text/x-signal-plain", "application/x-signal-view-once", "*/*", "application/octet-stream") 30 private val emojis = listOf("😂", "❤️", "🔥", "😍", "👀", "🤔", "🙏", "👍", "🤷", "🥺") 31 32 private val random = Random(1) 33 34 /** 35 * Create an [Envelope]. 36 */ 37 fun envelope(timestamp: Long, serverGuid: UUID = UUID.randomUUID()): Envelope { 38 return Envelope.Builder() 39 .timestamp(timestamp) 40 .serverTimestamp(timestamp + 5) 41 .serverGuid(serverGuid.toString()) 42 .build() 43 } 44 45 /** 46 * Create metadata to match an [Envelope]. 47 */ 48 fun envelopeMetadata(source: RecipientId, destination: RecipientId, sourceDeviceId: Int = 1, groupId: GroupId.V2? = null): EnvelopeMetadata { 49 return EnvelopeMetadata( 50 sourceServiceId = Recipient.resolved(source).requireServiceId(), 51 sourceE164 = null, 52 sourceDeviceId = sourceDeviceId, 53 sealedSender = true, 54 groupId = groupId?.decodedId, 55 destinationServiceId = Recipient.resolved(destination).requireServiceId() 56 ) 57 } 58 59 /** 60 * Create a random text message that will contain a body but may also contain 61 * - An expire timer value 62 * - Bold style body ranges 63 */ 64 fun fuzzTextMessage(sentTimestamp: Long? = null, groupContextV2: GroupContextV2? = null): Content { 65 return Content.Builder() 66 .dataMessage( 67 DataMessage.Builder().buildWith { 68 timestamp = sentTimestamp 69 body = string() 70 if (random.nextBoolean()) { 71 expireTimer = random.nextInt(0..28.days.inWholeSeconds.toInt()) 72 } 73 if (random.nextBoolean()) { 74 bodyRanges( 75 listOf( 76 BodyRange.Builder().buildWith { 77 start = 0 78 length = 1 79 style = BodyRange.Style.BOLD 80 } 81 ) 82 ) 83 } 84 if (groupContextV2 != null) { 85 groupV2 = groupContextV2 86 } 87 } 88 ) 89 .build() 90 } 91 92 /** 93 * Create an edit message. 94 */ 95 fun editTextMessage(targetTimestamp: Long, editedDataMessage: DataMessage): Content { 96 return Content.Builder() 97 .editMessage( 98 EditMessage.Builder().buildWith { 99 targetSentTimestamp = targetTimestamp 100 dataMessage = editedDataMessage 101 } 102 ) 103 .build() 104 } 105 106 /** 107 * Create a sync sent text message for the given [DataMessage]. 108 */ 109 fun syncSentTextMessage( 110 textMessage: DataMessage, 111 deliveredTo: List<RecipientId>, 112 recipientUpdate: Boolean = false 113 ): Content { 114 return Content 115 .Builder() 116 .syncMessage( 117 SyncMessage.Builder().buildWith { 118 sent = SyncMessage.Sent.Builder().buildWith { 119 timestamp = textMessage.timestamp 120 message = textMessage 121 isRecipientUpdate = recipientUpdate 122 unidentifiedStatus( 123 deliveredTo.map { 124 SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder().buildWith { 125 destinationServiceId = Recipient.resolved(it).requireServiceId().toString() 126 unidentified = true 127 } 128 } 129 ) 130 } 131 } 132 ).build() 133 } 134 135 /** 136 * Create a sync reads message for the given [RecipientId] and message timestamp pairings. 137 */ 138 fun syncReadsMessage(timestamps: List<Pair<RecipientId, Long>>): Content { 139 return Content 140 .Builder() 141 .syncMessage( 142 SyncMessage.Builder().buildWith { 143 read = timestamps.map { (senderId, timestamp) -> 144 SyncMessage.Read.Builder().buildWith { 145 this.senderAci = Recipient.resolved(senderId).requireAci().toString() 146 this.timestamp = timestamp 147 } 148 } 149 } 150 ).build() 151 } 152 153 /** 154 * Create a random media message that may be: 155 * - A text body 156 * - A text body with a quote that references an existing message 157 * - A text body with a quote that references a non existing message 158 * - A message with 0-2 attachment pointers and may contain a text body 159 */ 160 fun fuzzMediaMessageWithBody(quoteAble: List<TestMessage> = emptyList()): Content { 161 return Content.Builder() 162 .dataMessage( 163 DataMessage.Builder().buildWith { 164 if (random.nextBoolean()) { 165 body = string() 166 } 167 168 if (random.nextBoolean() && quoteAble.isNotEmpty()) { 169 body = string() 170 val quoted = quoteAble.random(random) 171 quote = DataMessage.Quote.Builder().buildWith { 172 id = quoted.envelope.timestamp 173 authorAci = quoted.metadata.sourceServiceId.toString() 174 text = quoted.content.dataMessage?.body 175 attachments(quoted.content.dataMessage?.attachments ?: emptyList()) 176 bodyRanges(quoted.content.dataMessage?.bodyRanges ?: emptyList()) 177 type = DataMessage.Quote.Type.NORMAL 178 } 179 } 180 181 if (random.nextFloat() < 0.1 && quoteAble.isNotEmpty()) { 182 val quoted = quoteAble.random(random) 183 quote = DataMessage.Quote.Builder().buildWith { 184 id = random.nextLong(quoted.envelope.timestamp!! - 1000000, quoted.envelope.timestamp!!) 185 authorAci = quoted.metadata.sourceServiceId.toString() 186 text = quoted.content.dataMessage?.body 187 } 188 } 189 190 if (random.nextFloat() < 0.25) { 191 val total = random.nextInt(1, 2) 192 attachments((0..total).map { attachmentPointer() }) 193 } 194 } 195 ) 196 .build() 197 } 198 199 /** 200 * Creates a random media message that contains no traditional media content. It may be: 201 * - A reaction to a prior message 202 */ 203 fun fuzzMediaMessageNoContent(previousMessages: List<TestMessage> = emptyList()): Content { 204 return Content.Builder() 205 .dataMessage( 206 DataMessage.Builder().buildWith { 207 if (random.nextFloat() < 0.25) { 208 val reactTo = previousMessages.random(random) 209 reaction = DataMessage.Reaction.Builder().buildWith { 210 emoji = emojis.random(random) 211 remove = false 212 targetAuthorAci = reactTo.metadata.sourceServiceId.toString() 213 targetSentTimestamp = reactTo.envelope.timestamp 214 } 215 } 216 } 217 ).build() 218 } 219 220 /** 221 * Create a random media message that contains a sticker. 222 */ 223 fun fuzzStickerMediaMessage(sentTimestamp: Long? = null, groupContextV2: GroupContextV2? = null): Content { 224 return Content.Builder() 225 .dataMessage( 226 DataMessage.Builder().buildWith { 227 timestamp = sentTimestamp 228 sticker = DataMessage.Sticker.Builder().buildWith { 229 packId = byteString(length = 24) 230 packKey = byteString(length = 128) 231 stickerId = random.nextInt() 232 data_ = attachmentPointer() 233 emoji = emojis.random(random) 234 } 235 groupV2 = groupContextV2 236 } 237 ).build() 238 } 239 240 /** 241 * Generate a random [String]. 242 */ 243 fun string(length: Int = 10, allowNullString: Boolean = false): String { 244 var string = "" 245 246 if (allowNullString && random.nextBoolean()) { 247 return string 248 } 249 250 for (i in 0 until length) { 251 string += random.nextInt(65..90).toChar() 252 } 253 return string 254 } 255 256 /** 257 * Generate a random [ByteString]. 258 */ 259 fun byteString(length: Int = 512): ByteString { 260 return random.nextBytes(length).toByteString() 261 } 262 263 /** 264 * Generate a random [AttachmentPointer]. 265 */ 266 fun attachmentPointer(): AttachmentPointer { 267 return AttachmentPointer.Builder().run { 268 cdnKey = string() 269 contentType = mediaTypes.random(random) 270 key = byteString() 271 size = random.nextInt(1024 * 1024 * 50) 272 thumbnail = byteString() 273 digest = byteString() 274 fileName = string() 275 flags = 0 276 width = random.nextInt(until = 1024) 277 height = random.nextInt(until = 1024) 278 caption = string(allowNullString = true) 279 blurHash = string() 280 uploadTimestamp = random.nextLong() 281 cdnNumber = 1 282 283 build() 284 } 285 } 286 287 /** 288 * Creates a server delivered timestamp that is always later than the envelope and server "received" timestamp. 289 */ 290 fun fuzzServerDeliveredTimestamp(envelopeTimestamp: Long): Long { 291 return envelopeTimestamp + 10 292 } 293}