112 lines
5.9 KiB
Kotlin
Raw Normal View History

2020-05-11 16:19:26 +10:00
package org.thoughtcrime.securesms.loki.protocol
import android.content.Context
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.map
2020-05-13 10:24:20 +10:00
import org.thoughtcrime.securesms.ApplicationContext
2020-05-12 16:28:35 +10:00
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore
2020-05-11 16:19:26 +10:00
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
2020-05-13 12:29:31 +10:00
import org.thoughtcrime.securesms.loki.utilities.recipient
import org.thoughtcrime.securesms.loki.utilities.timeout
2020-05-11 16:19:26 +10:00
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.GroupUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
2020-05-12 16:28:35 +10:00
import org.whispersystems.libsignal.SignalProtocolAddress
2020-05-13 12:29:31 +10:00
import org.whispersystems.signalservice.api.messages.SignalServiceContent
2020-05-12 16:28:35 +10:00
import org.whispersystems.signalservice.api.messages.SignalServiceGroup
import org.whispersystems.signalservice.api.push.SignalServiceAddress
2020-07-08 17:05:26 +10:00
import org.whispersystems.signalservice.loki.api.SnodeAPI
2020-07-15 12:24:43 +10:00
import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
2020-08-04 15:33:37 +10:00
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol
2020-05-22 10:41:31 +10:00
import java.util.*
2020-05-11 16:19:26 +10:00
object ClosedGroupsProtocol {
/**
* Blocks the calling thread.
*/
2020-05-12 16:28:35 +10:00
@JvmStatic
2020-05-13 12:29:31 +10:00
fun shouldIgnoreContentMessage(context: Context, conversation: Recipient, groupID: String?, content: SignalServiceContent): Boolean {
if (!conversation.address.isClosedGroup || groupID == null) { return false }
2020-05-14 11:44:33 +10:00
// A closed group's members should never include slave devices
2020-05-13 12:29:31 +10:00
val senderPublicKey = content.sender
2020-07-15 12:24:43 +10:00
FileServerAPI.shared.getDeviceLinks(senderPublicKey).timeout(6000).get()
2020-05-13 12:29:31 +10:00
val senderMasterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(senderPublicKey)
val publicKeyToCheckFor = senderMasterPublicKey ?: senderPublicKey
val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, true)
return !members.contains(recipient(context, publicKeyToCheckFor))
}
@JvmStatic
fun shouldIgnoreGroupCreatedMessage(context: Context, group: SignalServiceGroup): Boolean {
2020-05-12 16:28:35 +10:00
val members = group.members
2020-05-22 11:57:40 +10:00
val masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
val masterPublicKey = masterPublicKeyOrNull ?: TextSecurePreferences.getLocalNumber(context)
return !members.isPresent || !members.get().contains(masterPublicKey)
2020-05-12 16:28:35 +10:00
}
2020-05-12 15:29:00 +10:00
@JvmStatic
fun getDestinations(groupID: String, context: Context): Promise<List<Address>, Exception> {
if (GroupUtil.isRSSFeed(groupID)) { return Promise.of(listOf()) }
2020-05-12 15:29:00 +10:00
if (GroupUtil.isOpenGroup(groupID)) {
val result = mutableListOf<Address>()
result.add(Address.fromSerialized(groupID))
return Promise.of(result)
2020-05-12 15:29:00 +10:00
} else {
2020-05-13 12:29:31 +10:00
// A closed group's members should never include slave devices
2020-05-12 15:29:00 +10:00
val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, false)
2020-07-15 12:24:43 +10:00
return FileServerAPI.shared.getDeviceLinks(members.map { it.address.serialize() }.toSet()).map {
val result = members.flatMap { member ->
MultiDeviceProtocol.shared.getAllLinkedDevices(member.address.serialize()).map { Address.fromSerialized(it) }
}.toMutableSet()
val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
if (userMasterPublicKey != null && result.contains(Address.fromSerialized(userMasterPublicKey))) {
result.remove(Address.fromSerialized(userMasterPublicKey))
}
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
if (userPublicKey != null && result.contains(Address.fromSerialized(userPublicKey))) {
result.remove(Address.fromSerialized(userPublicKey))
}
result.toList()
2020-05-12 15:29:00 +10:00
}
}
}
@JvmStatic
2020-05-11 16:19:26 +10:00
fun leaveGroup(context: Context, recipient: Recipient): Boolean {
2020-05-11 16:54:31 +10:00
if (!recipient.address.isClosedGroup) { return true }
2020-05-11 16:19:26 +10:00
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
val message = GroupUtil.createGroupLeaveMessage(context, recipient)
if (threadID < 0 || !message.isPresent) { return false }
MessageSender.send(context, message.get(), threadID, false, null)
2020-05-13 12:29:31 +10:00
// Remove the master device from the group (a closed group's members should never include slave devices)
2020-05-11 16:54:31 +10:00
val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
2020-05-13 12:29:31 +10:00
val publicKeyToRemove = masterPublicKey ?: TextSecurePreferences.getLocalNumber(context)
2020-05-11 16:19:26 +10:00
val groupDatabase = DatabaseFactory.getGroupDatabase(context)
val groupID = recipient.address.toGroupString()
groupDatabase.setActive(groupID, false)
2020-05-13 12:29:31 +10:00
groupDatabase.remove(groupID, Address.fromSerialized(publicKeyToRemove))
2020-05-11 16:19:26 +10:00
return true
}
2020-05-12 16:28:35 +10:00
@JvmStatic
fun establishSessionsWithMembersIfNeeded(context: Context, members: List<String>) {
2020-05-13 12:29:31 +10:00
// A closed group's members should never include slave devices
2020-05-12 16:28:35 +10:00
val allDevices = members.flatMap { member ->
MultiDeviceProtocol.shared.getAllLinkedDevices(member)
}.toMutableSet()
2020-05-13 12:29:31 +10:00
val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
if (userMasterPublicKey != null && allDevices.contains(userMasterPublicKey)) {
allDevices.remove(userMasterPublicKey)
2020-05-12 16:28:35 +10:00
}
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
if (userPublicKey != null && allDevices.contains(userPublicKey)) {
allDevices.remove(userPublicKey)
}
for (device in allDevices) {
2020-07-15 12:24:43 +10:00
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(device)
2020-05-12 16:28:35 +10:00
}
}
2020-05-11 16:19:26 +10:00
}