|
|
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki.protocol
|
|
|
|
|
|
|
|
|
|
import android.content.Context
|
|
|
|
|
import android.util.Log
|
|
|
|
|
import androidx.annotation.WorkerThread
|
|
|
|
|
import com.google.protobuf.ByteString
|
|
|
|
|
import nl.komponents.kovenant.Promise
|
|
|
|
|
import nl.komponents.kovenant.deferred
|
|
|
|
@ -129,19 +130,16 @@ object ClosedGroupsProtocolV2 {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@JvmStatic
|
|
|
|
|
fun explicitAddMembers(context: Context, groupPublicKey: String, membersToAdd: List<String>): Promise<Any, java.lang.Exception> {
|
|
|
|
|
return task {
|
|
|
|
|
fun explicitAddMembers(context: Context, groupPublicKey: String, membersToAdd: List<String>) {
|
|
|
|
|
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
|
|
|
|
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
|
|
|
val groupID = doubleEncodeGroupID(groupPublicKey)
|
|
|
|
|
val group = groupDB.getGroup(groupID).orNull()
|
|
|
|
|
if (group == null) {
|
|
|
|
|
Log.d("Loki", "Can't leave nonexistent closed group.")
|
|
|
|
|
return@task Error.NoThread
|
|
|
|
|
throw Error.NoThread
|
|
|
|
|
}
|
|
|
|
|
val updatedMembers = group.members.map { it.serialize() }.toSet() + membersToAdd
|
|
|
|
|
// Save the new group members
|
|
|
|
|
groupDB.updateMembers(groupID, updatedMembers.map { Address.fromSerialized(it) })
|
|
|
|
|
val membersAsData = updatedMembers.map { Hex.fromStringCondensed(it) }
|
|
|
|
|
val newMembersAsData = membersToAdd.map { Hex.fromStringCondensed(it) }
|
|
|
|
|
val admins = group.admins.map { it.serialize() }
|
|
|
|
@ -152,7 +150,7 @@ object ClosedGroupsProtocolV2 {
|
|
|
|
|
}.orNull()
|
|
|
|
|
if (encryptionKeyPair == null) {
|
|
|
|
|
Log.d("Loki", "Couldn't get encryption key pair for closed group.")
|
|
|
|
|
return@task Error.NoKeyPair
|
|
|
|
|
throw Error.NoKeyPair
|
|
|
|
|
}
|
|
|
|
|
val name = group.title
|
|
|
|
|
// Send the update to the group
|
|
|
|
@ -160,6 +158,8 @@ object ClosedGroupsProtocolV2 {
|
|
|
|
|
val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, memberUpdateKind, sentTime)
|
|
|
|
|
job.setContext(context)
|
|
|
|
|
job.onRun() // Run the job immediately
|
|
|
|
|
// Save the new group members
|
|
|
|
|
groupDB.updateMembers(groupID, updatedMembers.map { Address.fromSerialized(it) })
|
|
|
|
|
// Notify the user
|
|
|
|
|
val infoType = GroupContext.Type.UPDATE
|
|
|
|
|
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false))
|
|
|
|
@ -173,11 +173,9 @@ object ClosedGroupsProtocolV2 {
|
|
|
|
|
ApplicationContext.getInstance(context).jobManager.add(newMemberJob)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@JvmStatic
|
|
|
|
|
fun explicitRemoveMembers(context: Context, groupPublicKey: String, membersToRemove: List<String>): Promise<Any, Exception> {
|
|
|
|
|
return task {
|
|
|
|
|
fun explicitRemoveMembers(context: Context, groupPublicKey: String, membersToRemove: List<String>) {
|
|
|
|
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
|
|
|
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
|
|
|
|
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
|
|
@ -185,22 +183,20 @@ object ClosedGroupsProtocolV2 {
|
|
|
|
|
val group = groupDB.getGroup(groupID).orNull()
|
|
|
|
|
if (group == null) {
|
|
|
|
|
Log.d("Loki", "Can't leave nonexistent closed group.")
|
|
|
|
|
return@task Error.NoThread
|
|
|
|
|
throw Error.NoThread
|
|
|
|
|
}
|
|
|
|
|
val updatedMembers = group.members.map { it.serialize() }.toSet() - membersToRemove
|
|
|
|
|
// Save the new group members
|
|
|
|
|
groupDB.updateMembers(groupID, updatedMembers.map { Address.fromSerialized(it) })
|
|
|
|
|
val removeMembersAsData = membersToRemove.map { Hex.fromStringCondensed(it) }
|
|
|
|
|
val admins = group.admins.map { it.serialize() }
|
|
|
|
|
val sentTime = System.currentTimeMillis()
|
|
|
|
|
val encryptionKeyPair = apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey)
|
|
|
|
|
if (encryptionKeyPair == null) {
|
|
|
|
|
Log.d("Loki", "Couldn't get encryption key pair for closed group.")
|
|
|
|
|
return@task Error.NoKeyPair
|
|
|
|
|
throw Error.NoKeyPair
|
|
|
|
|
}
|
|
|
|
|
if (membersToRemove.any { it in admins } && updatedMembers.isNotEmpty()) {
|
|
|
|
|
Log.d("Loki", "Can't remove admin from closed group unless the group is destroyed entirely.")
|
|
|
|
|
return@task Error.InvalidUpdate
|
|
|
|
|
throw Error.InvalidUpdate
|
|
|
|
|
}
|
|
|
|
|
val name = group.title
|
|
|
|
|
// Send the update to the group
|
|
|
|
@ -208,6 +204,8 @@ object ClosedGroupsProtocolV2 {
|
|
|
|
|
val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, memberUpdateKind, sentTime)
|
|
|
|
|
job.setContext(context)
|
|
|
|
|
job.onRun() // Run the job immediately
|
|
|
|
|
// Save the new group members
|
|
|
|
|
groupDB.updateMembers(groupID, updatedMembers.map { Address.fromSerialized(it) })
|
|
|
|
|
// Notify the user
|
|
|
|
|
val infoType = GroupContext.Type.UPDATE
|
|
|
|
|
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false))
|
|
|
|
@ -216,14 +214,10 @@ object ClosedGroupsProtocolV2 {
|
|
|
|
|
if (isCurrentUserAdmin) {
|
|
|
|
|
generateAndSendNewEncryptionKeyPair(context, groupPublicKey, updatedMembers)
|
|
|
|
|
}
|
|
|
|
|
return@task Unit
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@JvmStatic
|
|
|
|
|
fun explicitNameChange(context: Context, groupPublicKey: String, newName: String): Promise<Unit, Exception> {
|
|
|
|
|
val deferred = deferred<Unit, Exception>()
|
|
|
|
|
ThreadUtils.queue {
|
|
|
|
|
fun explicitNameChange(context: Context, groupPublicKey: String, newName: String) {
|
|
|
|
|
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
|
|
|
val groupID = doubleEncodeGroupID(groupPublicKey)
|
|
|
|
|
val group = groupDB.getGroup(groupID).orNull()
|
|
|
|
@ -232,7 +226,7 @@ object ClosedGroupsProtocolV2 {
|
|
|
|
|
val sentTime = System.currentTimeMillis()
|
|
|
|
|
if (group == null) {
|
|
|
|
|
Log.d("Loki", "Can't leave nonexistent closed group.")
|
|
|
|
|
return@queue deferred.reject(Error.NoThread)
|
|
|
|
|
throw Error.NoThread
|
|
|
|
|
}
|
|
|
|
|
// Send the update to the group
|
|
|
|
|
val kind = ClosedGroupUpdateMessageSendJobV2.Kind.NameChange(newName)
|
|
|
|
@ -245,31 +239,6 @@ object ClosedGroupsProtocolV2 {
|
|
|
|
|
insertOutgoingInfoMessage(context, groupID, infoType, newName, members, admins, threadID, sentTime)
|
|
|
|
|
// Update the group
|
|
|
|
|
groupDB.updateTitle(groupID, newName)
|
|
|
|
|
deferred.resolve(Unit)
|
|
|
|
|
}
|
|
|
|
|
return deferred.promise
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@JvmStatic
|
|
|
|
|
fun leave(context: Context, groupPublicKey: String): Promise<Unit, Exception> {
|
|
|
|
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
|
|
|
|
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
|
|
|
val groupID = doubleEncodeGroupID(groupPublicKey)
|
|
|
|
|
val group = groupDB.getGroup(groupID).orNull()
|
|
|
|
|
if (group == null) {
|
|
|
|
|
Log.d("Loki", "Can't leave nonexistent closed group.")
|
|
|
|
|
return Promise.ofFail(Error.NoThread)
|
|
|
|
|
}
|
|
|
|
|
val name = group.title
|
|
|
|
|
val oldMembers = group.members.map { it.serialize() }.toSet()
|
|
|
|
|
val newMembers: Set<String>
|
|
|
|
|
val isCurrentUserAdmin = group.admins.map { it.toString() }.contains(userPublicKey)
|
|
|
|
|
if (!isCurrentUserAdmin) {
|
|
|
|
|
newMembers = oldMembers.minus(userPublicKey)
|
|
|
|
|
} else {
|
|
|
|
|
newMembers = setOf() // If the admin leaves the group is destroyed
|
|
|
|
|
}
|
|
|
|
|
return update(context, groupPublicKey, newMembers, name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun update(context: Context, groupPublicKey: String, members: Collection<String>, name: String): Promise<Unit, Exception> {
|
|
|
|
|