mirror of
https://github.com/oxen-io/session-android.git
synced 2025-04-21 18:21:30 +00:00
Rename to remove api version suffix
This commit is contained in:
parent
0674e8090d
commit
95fd2baec0
@ -57,7 +57,7 @@ import org.session.libsession.messaging.messages.control.DataExtractionNotificat
|
|||||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage
|
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage
|
||||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage
|
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
||||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
||||||
@ -500,7 +500,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
|
|
||||||
private fun getLatestOpenGroupInfoIfNeeded() {
|
private fun getLatestOpenGroupInfoIfNeeded() {
|
||||||
val openGroup = lokiThreadDb.getOpenGroupChat(viewModel.threadId) ?: return
|
val openGroup = lokiThreadDb.getOpenGroupChat(viewModel.threadId) ?: return
|
||||||
OpenGroupApiV4.getMemberCount(openGroup.room, openGroup.server).successUi { updateSubtitle() }
|
OpenGroupApi.getMemberCount(openGroup.room, openGroup.server).successUi { updateSubtitle() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from onCreate
|
// called from onCreate
|
||||||
|
@ -7,7 +7,7 @@ import android.view.View
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import network.loki.messenger.databinding.ViewMentionCandidateBinding
|
import network.loki.messenger.databinding.ViewMentionCandidateBinding
|
||||||
import org.session.libsession.messaging.mentions.Mention
|
import org.session.libsession.messaging.mentions.Mention
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
|
|
||||||
class MentionCandidateView : LinearLayout {
|
class MentionCandidateView : LinearLayout {
|
||||||
@ -34,7 +34,7 @@ class MentionCandidateView : LinearLayout {
|
|||||||
profilePictureView.glide = glide!!
|
profilePictureView.glide = glide!!
|
||||||
profilePictureView.update()
|
profilePictureView.update()
|
||||||
if (openGroupServer != null && openGroupRoom != null) {
|
if (openGroupServer != null && openGroupRoom != null) {
|
||||||
val isUserModerator = OpenGroupApiV4.isUserModerator(mentionCandidate.publicKey, openGroupRoom!!, openGroupServer!!)
|
val isUserModerator = OpenGroupApi.isUserModerator(mentionCandidate.publicKey, openGroupRoom!!, openGroupServer!!)
|
||||||
moderatorIconImageView.visibility = if (isUserModerator) View.VISIBLE else View.GONE
|
moderatorIconImageView.visibility = if (isUserModerator) View.VISIBLE else View.GONE
|
||||||
} else {
|
} else {
|
||||||
moderatorIconImageView.visibility = View.GONE
|
moderatorIconImageView.visibility = View.GONE
|
||||||
|
@ -7,7 +7,7 @@ import android.view.View
|
|||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import network.loki.messenger.databinding.ViewMentionCandidateV2Binding
|
import network.loki.messenger.databinding.ViewMentionCandidateV2Binding
|
||||||
import org.session.libsession.messaging.mentions.Mention
|
import org.session.libsession.messaging.mentions.Mention
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
|
|
||||||
class MentionCandidateView : RelativeLayout {
|
class MentionCandidateView : RelativeLayout {
|
||||||
@ -34,7 +34,7 @@ class MentionCandidateView : RelativeLayout {
|
|||||||
profilePictureView.glide = glide!!
|
profilePictureView.glide = glide!!
|
||||||
profilePictureView.update()
|
profilePictureView.update()
|
||||||
if (openGroupServer != null && openGroupRoom != null) {
|
if (openGroupServer != null && openGroupRoom != null) {
|
||||||
val isUserModerator = OpenGroupApiV4.isUserModerator(candidate.publicKey, openGroupRoom!!, openGroupServer!!)
|
val isUserModerator = OpenGroupApi.isUserModerator(candidate.publicKey, openGroupRoom!!, openGroupServer!!)
|
||||||
moderatorIconImageView.visibility = if (isUserModerator) View.VISIBLE else View.GONE
|
moderatorIconImageView.visibility = if (isUserModerator) View.VISIBLE else View.GONE
|
||||||
} else {
|
} else {
|
||||||
moderatorIconImageView.visibility = View.GONE
|
moderatorIconImageView.visibility = View.GONE
|
||||||
|
@ -5,7 +5,7 @@ import android.view.ActionMode
|
|||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationAdapter
|
import org.thoughtcrime.securesms.conversation.v2.ConversationAdapter
|
||||||
@ -41,13 +41,13 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
|
|||||||
if (!ConversationActivityV2.IS_UNSEND_REQUESTS_ENABLED) {
|
if (!ConversationActivityV2.IS_UNSEND_REQUESTS_ENABLED) {
|
||||||
if (openGroup == null) { return true }
|
if (openGroup == null) { return true }
|
||||||
if (allSentByCurrentUser) { return true }
|
if (allSentByCurrentUser) { return true }
|
||||||
return OpenGroupApiV4.isUserModerator(userPublicKey, openGroup.room, openGroup.server)
|
return OpenGroupApi.isUserModerator(userPublicKey, openGroup.room, openGroup.server)
|
||||||
}
|
}
|
||||||
|
|
||||||
val allReceivedByCurrentUser = selectedItems.all { !it.isOutgoing }
|
val allReceivedByCurrentUser = selectedItems.all { !it.isOutgoing }
|
||||||
if (openGroup == null) { return allSentByCurrentUser || allReceivedByCurrentUser }
|
if (openGroup == null) { return allSentByCurrentUser || allReceivedByCurrentUser }
|
||||||
if (allSentByCurrentUser) { return true }
|
if (allSentByCurrentUser) { return true }
|
||||||
return OpenGroupApiV4.isUserModerator(userPublicKey, openGroup.room, openGroup.server)
|
return OpenGroupApi.isUserModerator(userPublicKey, openGroup.room, openGroup.server)
|
||||||
}
|
}
|
||||||
fun userCanBanSelectedUsers(): Boolean {
|
fun userCanBanSelectedUsers(): Boolean {
|
||||||
if (openGroup == null) { return false }
|
if (openGroup == null) { return false }
|
||||||
@ -55,7 +55,7 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
|
|||||||
if (anySentByCurrentUser) { return false } // Users can't ban themselves
|
if (anySentByCurrentUser) { return false } // Users can't ban themselves
|
||||||
val selectedUsers = selectedItems.map { it.recipient.address.toString() }.toSet()
|
val selectedUsers = selectedItems.map { it.recipient.address.toString() }.toSet()
|
||||||
if (selectedUsers.size > 1) { return false }
|
if (selectedUsers.size > 1) { return false }
|
||||||
return OpenGroupApiV4.isUserModerator(userPublicKey, openGroup.room, openGroup.server)
|
return OpenGroupApi.isUserModerator(userPublicKey, openGroup.room, openGroup.server)
|
||||||
}
|
}
|
||||||
// Delete message
|
// Delete message
|
||||||
menu.findItem(R.id.menu_context_delete_message).isVisible = userCanDeleteSelectedItems()
|
menu.findItem(R.id.menu_context_delete_message).isVisible = userCanDeleteSelectedItems()
|
||||||
|
@ -24,7 +24,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ViewVisibleMessageBinding
|
import network.loki.messenger.databinding.ViewVisibleMessageBinding
|
||||||
import org.session.libsession.messaging.contacts.Contact.ContactContext
|
import org.session.libsession.messaging.contacts.Contact.ContactContext
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.utilities.ViewUtil
|
import org.session.libsession.utilities.ViewUtil
|
||||||
import org.session.libsignal.utilities.ThreadUtils
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
@ -127,7 +127,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
}
|
}
|
||||||
if (thread.isOpenGroupRecipient) {
|
if (thread.isOpenGroupRecipient) {
|
||||||
val openGroup = lokiThreadDb.getOpenGroupChat(threadID) ?: return
|
val openGroup = lokiThreadDb.getOpenGroupChat(threadID) ?: return
|
||||||
val isModerator = OpenGroupApiV4.isUserModerator(senderSessionID, openGroup.room, openGroup.server)
|
val isModerator = OpenGroupApi.isUserModerator(senderSessionID, openGroup.room, openGroup.server)
|
||||||
binding.moderatorIconImageView.visibility = if (isModerator) View.VISIBLE else View.INVISIBLE
|
binding.moderatorIconImageView.visibility = if (isModerator) View.VISIBLE else View.INVISIBLE
|
||||||
} else {
|
} else {
|
||||||
binding.moderatorIconImageView.visibility = View.INVISIBLE
|
binding.moderatorIconImageView.visibility = View.INVISIBLE
|
||||||
|
@ -4,19 +4,19 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.thoughtcrime.securesms.util.State
|
import org.thoughtcrime.securesms.util.State
|
||||||
|
|
||||||
typealias DefaultGroups = List<OpenGroupApiV4.DefaultGroup>
|
typealias DefaultGroups = List<OpenGroupApi.DefaultGroup>
|
||||||
typealias GroupState = State<DefaultGroups>
|
typealias GroupState = State<DefaultGroups>
|
||||||
|
|
||||||
class DefaultGroupsViewModel : ViewModel() {
|
class DefaultGroupsViewModel : ViewModel() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
OpenGroupApiV4.getDefaultRoomsIfNeeded()
|
OpenGroupApi.getDefaultRoomsIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
val defaultRooms = OpenGroupApiV4.defaultRooms.map<DefaultGroups, GroupState> {
|
val defaultRooms = OpenGroupApi.defaultRooms.map<DefaultGroups, GroupState> {
|
||||||
State.Success(it)
|
State.Success(it)
|
||||||
}.onStart {
|
}.onStart {
|
||||||
emit(State.Loading)
|
emit(State.Loading)
|
||||||
|
@ -25,7 +25,7 @@ import network.loki.messenger.R
|
|||||||
import network.loki.messenger.databinding.ActivityJoinPublicChatBinding
|
import network.loki.messenger.databinding.ActivityJoinPublicChatBinding
|
||||||
import network.loki.messenger.databinding.FragmentEnterChatUrlBinding
|
import network.loki.messenger.databinding.FragmentEnterChatUrlBinding
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4.DefaultGroup
|
import org.session.libsession.messaging.open_groups.OpenGroupApi.DefaultGroup
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
@ -4,9 +4,9 @@ import android.content.Context
|
|||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPollerV4
|
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
|
||||||
import org.session.libsession.utilities.Util
|
import org.session.libsession.utilities.Util
|
||||||
import org.session.libsignal.utilities.ThreadUtils
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
@ -14,7 +14,7 @@ import java.util.concurrent.Executors
|
|||||||
|
|
||||||
object OpenGroupManager {
|
object OpenGroupManager {
|
||||||
private val executorService = Executors.newScheduledThreadPool(4)
|
private val executorService = Executors.newScheduledThreadPool(4)
|
||||||
private var pollers = mutableMapOf<String, OpenGroupPollerV4>() // One for each server
|
private var pollers = mutableMapOf<String, OpenGroupPoller>() // One for each server
|
||||||
private var isPolling = false
|
private var isPolling = false
|
||||||
|
|
||||||
val isAllCaughtUp: Boolean
|
val isAllCaughtUp: Boolean
|
||||||
@ -41,7 +41,7 @@ object OpenGroupManager {
|
|||||||
val servers = storage.getAllV2OpenGroups().values.map { it.server }.toSet()
|
val servers = storage.getAllV2OpenGroups().values.map { it.server }.toSet()
|
||||||
servers.forEach { server ->
|
servers.forEach { server ->
|
||||||
pollers[server]?.stop() // Shouldn't be necessary
|
pollers[server]?.stop() // Shouldn't be necessary
|
||||||
val poller = OpenGroupPollerV4(server, executorService)
|
val poller = OpenGroupPoller(server, executorService)
|
||||||
poller.startIfNeeded()
|
poller.startIfNeeded()
|
||||||
pollers[server] = poller
|
pollers[server] = poller
|
||||||
}
|
}
|
||||||
@ -67,11 +67,11 @@ object OpenGroupManager {
|
|||||||
// Store the public key
|
// Store the public key
|
||||||
storage.setOpenGroupPublicKey(server,publicKey)
|
storage.setOpenGroupPublicKey(server,publicKey)
|
||||||
// Get an auth token
|
// Get an auth token
|
||||||
OpenGroupApiV4.getAuthToken(room, server).get()
|
OpenGroupApi.getAuthToken(room, server).get()
|
||||||
// Get capabilities
|
// Get capabilities
|
||||||
val capabilities = OpenGroupApiV4.getCapabilities(room, server).get()
|
val capabilities = OpenGroupApi.getCapabilities(room, server).get()
|
||||||
// Get group info
|
// Get group info
|
||||||
val info = OpenGroupApiV4.getInfo(room, server).get()
|
val info = OpenGroupApi.getInfo(room, server).get()
|
||||||
// Create the group locally if not available already
|
// Create the group locally if not available already
|
||||||
if (threadID < 0) {
|
if (threadID < 0) {
|
||||||
threadID = GroupManager.createOpenGroup(openGroupID, context, null, info.name).threadId
|
threadID = GroupManager.createOpenGroup(openGroupID, context, null, info.name).threadId
|
||||||
@ -80,7 +80,7 @@ object OpenGroupManager {
|
|||||||
threadDB.setOpenGroupChat(openGroup, threadID)
|
threadDB.setOpenGroupChat(openGroup, threadID)
|
||||||
// Start the poller if needed
|
// Start the poller if needed
|
||||||
pollers[server]?.startIfNeeded() ?: run {
|
pollers[server]?.startIfNeeded() ?: run {
|
||||||
val poller = OpenGroupPollerV4(server, executorService)
|
val poller = OpenGroupPoller(server, executorService)
|
||||||
Util.runOnMain { poller.startIfNeeded() }
|
Util.runOnMain { poller.startIfNeeded() }
|
||||||
pollers[server] = poller
|
pollers[server] = poller
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,7 @@ package org.thoughtcrime.securesms.groups
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
@ -30,8 +29,8 @@ object OpenGroupUtilities {
|
|||||||
throw IllegalStateException("Attempt to update open group info for non-existent DB record: $groupId")
|
throw IllegalStateException("Attempt to update open group info for non-existent DB record: $groupId")
|
||||||
}
|
}
|
||||||
|
|
||||||
val info = OpenGroupApiV4.getInfo(room, server).get() // store info again?
|
val info = OpenGroupApi.getInfo(room, server).get() // store info again?
|
||||||
OpenGroupApiV4.getMemberCount(room, server).get()
|
OpenGroupApi.getMemberCount(room, server).get()
|
||||||
|
|
||||||
EventBus.getDefault().post(GroupInfoUpdatedEvent(server, room = room))
|
EventBus.getDefault().post(GroupInfoUpdatedEvent(server, room = room))
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import nl.komponents.kovenant.functional.map
|
|||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2
|
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPollerV4
|
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
|
||||||
import org.session.libsession.snode.SnodeAPI
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
@ -74,7 +74,7 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor
|
|||||||
val v2OpenGroupServers = v2OpenGroups.map { it.value.server }.toSet()
|
val v2OpenGroupServers = v2OpenGroups.map { it.value.server }.toSet()
|
||||||
|
|
||||||
for (server in v2OpenGroupServers) {
|
for (server in v2OpenGroupServers) {
|
||||||
val poller = OpenGroupPollerV4(server, null)
|
val poller = OpenGroupPoller(server, null)
|
||||||
poller.hasStarted = true
|
poller.hasStarted = true
|
||||||
promises.add(poller.poll())
|
promises.add(poller.poll())
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import org.session.libsession.messaging.messages.control.UnsendRequest
|
|||||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage
|
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage
|
||||||
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation
|
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
import org.session.libsession.snode.SnodeAPI
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
@ -154,7 +154,7 @@ class DefaultConversationRepository @Inject constructor(
|
|||||||
val openGroup = lokiThreadDb.getOpenGroupChat(threadId)
|
val openGroup = lokiThreadDb.getOpenGroupChat(threadId)
|
||||||
if (openGroup != null) {
|
if (openGroup != null) {
|
||||||
lokiMessageDb.getServerID(message.id, !message.isMms)?.let { messageServerID ->
|
lokiMessageDb.getServerID(message.id, !message.isMms)?.let { messageServerID ->
|
||||||
OpenGroupApiV4.deleteMessage(messageServerID, openGroup.room, openGroup.server)
|
OpenGroupApi.deleteMessage(messageServerID, openGroup.room, openGroup.server)
|
||||||
.success {
|
.success {
|
||||||
messageDataProvider.deleteMessage(message.id, !message.isMms)
|
messageDataProvider.deleteMessage(message.id, !message.isMms)
|
||||||
continuation.resume(ResultOf.Success(Unit))
|
continuation.resume(ResultOf.Success(Unit))
|
||||||
@ -206,7 +206,7 @@ class DefaultConversationRepository @Inject constructor(
|
|||||||
messageServerIDs[messageServerID] = message
|
messageServerIDs[messageServerID] = message
|
||||||
}
|
}
|
||||||
for ((messageServerID, message) in messageServerIDs) {
|
for ((messageServerID, message) in messageServerIDs) {
|
||||||
OpenGroupApiV4.deleteMessage(messageServerID, openGroup.room, openGroup.server)
|
OpenGroupApi.deleteMessage(messageServerID, openGroup.room, openGroup.server)
|
||||||
.success {
|
.success {
|
||||||
messageDataProvider.deleteMessage(message.id, !message.isMms)
|
messageDataProvider.deleteMessage(message.id, !message.isMms)
|
||||||
}.fail { error ->
|
}.fail { error ->
|
||||||
@ -229,7 +229,7 @@ class DefaultConversationRepository @Inject constructor(
|
|||||||
suspendCoroutine { continuation ->
|
suspendCoroutine { continuation ->
|
||||||
val sessionID = recipient.address.toString()
|
val sessionID = recipient.address.toString()
|
||||||
val openGroup = lokiThreadDb.getOpenGroupChat(threadId)!!
|
val openGroup = lokiThreadDb.getOpenGroupChat(threadId)!!
|
||||||
OpenGroupApiV4.ban(sessionID, openGroup.room, openGroup.server)
|
OpenGroupApi.ban(sessionID, openGroup.room, openGroup.server)
|
||||||
.success {
|
.success {
|
||||||
continuation.resume(ResultOf.Success(Unit))
|
continuation.resume(ResultOf.Success(Unit))
|
||||||
}.fail { error ->
|
}.fail { error ->
|
||||||
@ -241,7 +241,7 @@ class DefaultConversationRepository @Inject constructor(
|
|||||||
suspendCoroutine { continuation ->
|
suspendCoroutine { continuation ->
|
||||||
val sessionID = recipient.address.toString()
|
val sessionID = recipient.address.toString()
|
||||||
val openGroup = lokiThreadDb.getOpenGroupChat(threadId)!!
|
val openGroup = lokiThreadDb.getOpenGroupChat(threadId)!!
|
||||||
OpenGroupApiV4.banAndDeleteAll(sessionID, openGroup.room, openGroup.server)
|
OpenGroupApi.banAndDeleteAll(sessionID, openGroup.room, openGroup.server)
|
||||||
.success {
|
.success {
|
||||||
continuation.resume(ResultOf.Success(Unit))
|
continuation.resume(ResultOf.Success(Unit))
|
||||||
}.fail { error ->
|
}.fail { error ->
|
||||||
|
@ -6,7 +6,7 @@ import okhttp3.Headers
|
|||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.MediaType
|
import okhttp3.MediaType
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.session.libsignal.utilities.Base64
|
import org.session.libsignal.utilities.Base64
|
||||||
import org.session.libsignal.utilities.HTTP
|
import org.session.libsignal.utilities.HTTP
|
||||||
@ -53,7 +53,7 @@ object FileServerApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun send(request: Request): Promise<Map<*, *>, Exception> {
|
private fun send(request: Request): Promise<Map<*, *>, Exception> {
|
||||||
val url = HttpUrl.parse(server) ?: return Promise.ofFail(OpenGroupApiV4.Error.InvalidURL)
|
val url = HttpUrl.parse(server) ?: return Promise.ofFail(OpenGroupApi.Error.InvalidURL)
|
||||||
val urlBuilder = HttpUrl.Builder()
|
val urlBuilder = HttpUrl.Builder()
|
||||||
.scheme(url.scheme())
|
.scheme(url.scheme())
|
||||||
.host(url.host())
|
.host(url.host())
|
||||||
@ -87,7 +87,7 @@ object FileServerApi {
|
|||||||
val parameters = mapOf( "file" to base64EncodedFile )
|
val parameters = mapOf( "file" to base64EncodedFile )
|
||||||
val request = Request(verb = HTTP.Verb.POST, endpoint = "files", parameters = parameters)
|
val request = Request(verb = HTTP.Verb.POST, endpoint = "files", parameters = parameters)
|
||||||
return send(request).map { json ->
|
return send(request).map { json ->
|
||||||
json["result"] as? Long ?: throw OpenGroupApiV4.Error.ParsingFailed
|
json["result"] as? Long ?: throw OpenGroupApi.Error.ParsingFailed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@ package org.session.libsession.messaging.jobs
|
|||||||
|
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||||
@ -18,7 +17,6 @@ import org.session.libsignal.utilities.Log
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.lang.NullPointerException
|
|
||||||
|
|
||||||
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) : Job {
|
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) : Job {
|
||||||
override var delegate: JobDelegate? = null
|
override var delegate: JobDelegate? = null
|
||||||
@ -103,7 +101,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
|
|||||||
} else {
|
} else {
|
||||||
val url = HttpUrl.parse(attachment.url)!!
|
val url = HttpUrl.parse(attachment.url)!!
|
||||||
val fileID = url.pathSegments().last()
|
val fileID = url.pathSegments().last()
|
||||||
OpenGroupApiV4.download(fileID.toLong(), openGroupV2.room, openGroupV2.server).get().let {
|
OpenGroupApi.download(fileID.toLong(), openGroupV2.room, openGroupV2.server).get().let {
|
||||||
tempFile.writeBytes(it)
|
tempFile.writeBytes(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import okio.Buffer
|
|||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.file_server.FileServerApi
|
import org.session.libsession.messaging.file_server.FileServerApi
|
||||||
import org.session.libsession.messaging.messages.Message
|
import org.session.libsession.messaging.messages.Message
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
import org.session.libsession.messaging.utilities.Data
|
import org.session.libsession.messaging.utilities.Data
|
||||||
import org.session.libsession.utilities.DecodedAudio
|
import org.session.libsession.utilities.DecodedAudio
|
||||||
@ -53,7 +53,7 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
|
|||||||
val v2OpenGroup = storage.getV2OpenGroup(threadID.toLong())
|
val v2OpenGroup = storage.getV2OpenGroup(threadID.toLong())
|
||||||
if (v2OpenGroup != null) {
|
if (v2OpenGroup != null) {
|
||||||
val keyAndResult = upload(attachment, v2OpenGroup.server, false) {
|
val keyAndResult = upload(attachment, v2OpenGroup.server, false) {
|
||||||
OpenGroupApiV4.upload(it, v2OpenGroup.room, v2OpenGroup.server)
|
OpenGroupApi.upload(it, v2OpenGroup.room, v2OpenGroup.server)
|
||||||
}
|
}
|
||||||
handleSuccess(attachment, keyAndResult.first, keyAndResult.second)
|
handleSuccess(attachment, keyAndResult.first, keyAndResult.second)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package org.session.libsession.messaging.jobs
|
package org.session.libsession.messaging.jobs
|
||||||
|
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.utilities.Data
|
import org.session.libsession.messaging.utilities.Data
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
|
|
||||||
@ -15,8 +15,8 @@ class GroupAvatarDownloadJob(val room: String, val server: String) : Job {
|
|||||||
override fun execute() {
|
override fun execute() {
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
try {
|
try {
|
||||||
val info = OpenGroupApiV4.getInfo(room, server).get()
|
val info = OpenGroupApi.getInfo(room, server).get()
|
||||||
val bytes = OpenGroupApiV4.downloadOpenGroupProfilePicture(info.id, server).get()
|
val bytes = OpenGroupApi.downloadOpenGroupProfilePicture(info.id, server).get()
|
||||||
val groupId = GroupUtil.getEncodedOpenGroupID("$server.$room".toByteArray())
|
val groupId = GroupUtil.getEncodedOpenGroupID("$server.$room".toByteArray())
|
||||||
storage.updateProfilePicture(groupId, bytes)
|
storage.updateProfilePicture(groupId, bytes)
|
||||||
storage.updateTimestampUpdated(groupId, System.currentTimeMillis())
|
storage.updateTimestampUpdated(groupId, System.currentTimeMillis())
|
||||||
|
@ -1,486 +0,0 @@
|
|||||||
package org.session.libsession.messaging.open_groups
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonNaming
|
|
||||||
import com.fasterxml.jackson.databind.type.TypeFactory
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import nl.komponents.kovenant.Promise
|
|
||||||
import nl.komponents.kovenant.functional.bind
|
|
||||||
import nl.komponents.kovenant.functional.map
|
|
||||||
import okhttp3.Headers
|
|
||||||
import okhttp3.HttpUrl
|
|
||||||
import okhttp3.MediaType
|
|
||||||
import okhttp3.RequestBody
|
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPollerV2
|
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
|
||||||
import org.session.libsession.utilities.AESGCM
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
|
||||||
import org.session.libsignal.utilities.*
|
|
||||||
import org.session.libsignal.utilities.Base64.*
|
|
||||||
import org.session.libsignal.utilities.HTTP.Verb.*
|
|
||||||
import org.whispersystems.curve25519.Curve25519
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object OpenGroupAPIV2 {
|
|
||||||
private val moderators: HashMap<String, Set<String>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs)
|
|
||||||
private val curve = Curve25519.getInstance(Curve25519.BEST)
|
|
||||||
val defaultRooms = MutableSharedFlow<List<DefaultGroup>>(replay = 1)
|
|
||||||
private val hasPerformedInitialPoll = mutableMapOf<String, Boolean>()
|
|
||||||
private var hasUpdatedLastOpenDate = false
|
|
||||||
|
|
||||||
private val timeSinceLastOpen by lazy {
|
|
||||||
val context = MessagingModuleConfiguration.shared.context
|
|
||||||
val lastOpenDate = TextSecurePreferences.getLastOpenTimeDate(context)
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
now - lastOpenDate
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val defaultServerPublicKey = "a03c383cf63c3c4efe67acc52112a6dd734b3a946b9545f488aaa93da7991238"
|
|
||||||
const val defaultServer = "http://116.203.70.33"
|
|
||||||
|
|
||||||
sealed class Error(message: String) : Exception(message) {
|
|
||||||
object Generic : Error("An error occurred.")
|
|
||||||
object ParsingFailed : Error("Invalid response.")
|
|
||||||
object DecryptionFailed : Error("Couldn't decrypt response.")
|
|
||||||
object SigningFailed : Error("Couldn't sign message.")
|
|
||||||
object InvalidURL : Error("Invalid URL.")
|
|
||||||
object NoPublicKey : Error("Couldn't find server public key.")
|
|
||||||
}
|
|
||||||
|
|
||||||
data class DefaultGroup(val id: String, val name: String, val image: ByteArray?) {
|
|
||||||
|
|
||||||
val joinURL: String get() = "$defaultServer/$id?public_key=$defaultServerPublicKey"
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Info(val id: String, val name: String, val imageID: String?)
|
|
||||||
|
|
||||||
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class)
|
|
||||||
data class CompactPollRequest(val roomID: String, val authToken: String, val fromDeletionServerID: Long?, val fromMessageServerID: Long?)
|
|
||||||
data class CompactPollResult(val messages: List<OpenGroupMessageV2>, val deletions: List<MessageDeletion>, val moderators: List<String>)
|
|
||||||
|
|
||||||
data class MessageDeletion(
|
|
||||||
@JsonProperty("id")
|
|
||||||
val id: Long = 0,
|
|
||||||
@JsonProperty("deleted_message_id")
|
|
||||||
val deletedMessageServerID: Long = 0
|
|
||||||
) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val empty = MessageDeletion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Request(
|
|
||||||
val verb: HTTP.Verb,
|
|
||||||
val room: String?,
|
|
||||||
val server: String,
|
|
||||||
val endpoint: String,
|
|
||||||
val queryParameters: Map<String, String> = mapOf(),
|
|
||||||
val parameters: Any? = null,
|
|
||||||
val headers: Map<String, String> = mapOf(),
|
|
||||||
val isAuthRequired: Boolean = true,
|
|
||||||
/**
|
|
||||||
* Always `true` under normal circumstances. You might want to disable
|
|
||||||
* this when running over Lokinet.
|
|
||||||
*/
|
|
||||||
val useOnionRouting: Boolean = true
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun createBody(parameters: Any?): RequestBody? {
|
|
||||||
if (parameters == null) return null
|
|
||||||
val parametersAsJSON = JsonUtil.toJson(parameters)
|
|
||||||
return RequestBody.create(MediaType.get("application/json"), parametersAsJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun send(request: Request): Promise<Map<*, *>, Exception> {
|
|
||||||
val url = HttpUrl.parse(request.server) ?: return Promise.ofFail(Error.InvalidURL)
|
|
||||||
val urlBuilder = HttpUrl.Builder()
|
|
||||||
.scheme(url.scheme())
|
|
||||||
.host(url.host())
|
|
||||||
.port(url.port())
|
|
||||||
.addPathSegments(request.endpoint)
|
|
||||||
if (request.verb == GET) {
|
|
||||||
for ((key, value) in request.queryParameters) {
|
|
||||||
urlBuilder.addQueryParameter(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fun execute(token: String?): Promise<Map<*, *>, Exception> {
|
|
||||||
val requestBuilder = okhttp3.Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.headers(Headers.of(request.headers))
|
|
||||||
if (request.isAuthRequired) {
|
|
||||||
if (token.isNullOrEmpty()) throw IllegalStateException("No auth token for request.")
|
|
||||||
requestBuilder.header("Authorization", token)
|
|
||||||
}
|
|
||||||
when (request.verb) {
|
|
||||||
GET -> requestBuilder.get()
|
|
||||||
PUT -> requestBuilder.put(createBody(request.parameters)!!)
|
|
||||||
POST -> requestBuilder.post(createBody(request.parameters)!!)
|
|
||||||
DELETE -> requestBuilder.delete(createBody(request.parameters))
|
|
||||||
}
|
|
||||||
if (!request.room.isNullOrEmpty()) {
|
|
||||||
requestBuilder.header("Room", request.room)
|
|
||||||
}
|
|
||||||
if (request.useOnionRouting) {
|
|
||||||
val publicKey = MessagingModuleConfiguration.shared.storage.getOpenGroupPublicKey(request.server)
|
|
||||||
?: return Promise.ofFail(Error.NoPublicKey)
|
|
||||||
return OnionRequestAPI.sendOnionRequest(requestBuilder.build(), request.server, publicKey).fail { e ->
|
|
||||||
// A 401 means that we didn't provide a (valid) auth token for a route that required one. We use this as an
|
|
||||||
// indication that the token we're using has expired. Note that a 403 has a different meaning; it means that
|
|
||||||
// we provided a valid token but it doesn't have a high enough permission level for the route in question.
|
|
||||||
if (e is OnionRequestAPI.HTTPRequestFailedAtDestinationException && e.statusCode == 401) {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
if (request.room != null) {
|
|
||||||
storage.removeAuthToken(request.room, request.server)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Promise.ofFail(IllegalStateException("It's currently not allowed to send non onion routed requests."))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return if (request.isAuthRequired) {
|
|
||||||
getAuthToken(request.room!!, request.server).bind { execute(it) }
|
|
||||||
} else {
|
|
||||||
execute(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun downloadOpenGroupProfilePicture(roomID: String, server: String): Promise<ByteArray, Exception> {
|
|
||||||
val request = Request(verb = GET, room = roomID, server = server, endpoint = "rooms/$roomID/image", isAuthRequired = false)
|
|
||||||
return send(request).map { json ->
|
|
||||||
val result = json["result"] as? String ?: throw Error.ParsingFailed
|
|
||||||
decode(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// region Authorization
|
|
||||||
fun getAuthToken(room: String, server: String): Promise<String, Exception> {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
return storage.getAuthToken(room, server)?.let {
|
|
||||||
Promise.of(it)
|
|
||||||
} ?: run {
|
|
||||||
requestNewAuthToken(room, server)
|
|
||||||
.bind { claimAuthToken(it, room, server) }
|
|
||||||
.success { authToken ->
|
|
||||||
storage.setAuthToken(room, server, authToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun requestNewAuthToken(room: String, server: String): Promise<String, Exception> {
|
|
||||||
val (publicKey, privateKey) = MessagingModuleConfiguration.shared.storage.getUserX25519KeyPair().let { it.publicKey.serialize() to it.privateKey.serialize() }
|
|
||||||
?: return Promise.ofFail(Error.Generic)
|
|
||||||
val queryParameters = mutableMapOf( "public_key" to publicKey.toHexString() )
|
|
||||||
val request = Request(GET, room, server, "auth_token_challenge", queryParameters, isAuthRequired = false, parameters = null)
|
|
||||||
return send(request).map { json ->
|
|
||||||
val challenge = json["challenge"] as? Map<*, *> ?: throw Error.ParsingFailed
|
|
||||||
val base64EncodedCiphertext = challenge["ciphertext"] as? String ?: throw Error.ParsingFailed
|
|
||||||
val base64EncodedEphemeralPublicKey = challenge["ephemeral_public_key"] as? String ?: throw Error.ParsingFailed
|
|
||||||
val ciphertext = decode(base64EncodedCiphertext)
|
|
||||||
val ephemeralPublicKey = decode(base64EncodedEphemeralPublicKey)
|
|
||||||
val symmetricKey = AESGCM.generateSymmetricKey(ephemeralPublicKey, privateKey)
|
|
||||||
val tokenAsData = try {
|
|
||||||
AESGCM.decrypt(ciphertext, symmetricKey)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw Error.DecryptionFailed
|
|
||||||
}
|
|
||||||
tokenAsData.toHexString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun claimAuthToken(authToken: String, room: String, server: String): Promise<String, Exception> {
|
|
||||||
val parameters = mapOf( "public_key" to MessagingModuleConfiguration.shared.storage.getUserPublicKey()!! )
|
|
||||||
val headers = mapOf( "Authorization" to authToken )
|
|
||||||
val request = Request(verb = POST, room = room, server = server, endpoint = "claim_auth_token",
|
|
||||||
parameters = parameters, headers = headers, isAuthRequired = false)
|
|
||||||
return send(request).map { authToken }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteAuthToken(room: String, server: String): Promise<Unit, Exception> {
|
|
||||||
val request = Request(verb = DELETE, room = room, server = server, endpoint = "auth_token")
|
|
||||||
return send(request).map {
|
|
||||||
MessagingModuleConfiguration.shared.storage.removeAuthToken(room, server)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Upload/Download
|
|
||||||
fun upload(file: ByteArray, room: String, server: String): Promise<Long, Exception> {
|
|
||||||
val base64EncodedFile = encodeBytes(file)
|
|
||||||
val parameters = mapOf( "file" to base64EncodedFile )
|
|
||||||
val request = Request(verb = POST, room = room, server = server, endpoint = "files", parameters = parameters)
|
|
||||||
return send(request).map { json ->
|
|
||||||
(json["result"] as? Number)?.toLong() ?: throw Error.ParsingFailed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun download(file: Long, room: String, server: String): Promise<ByteArray, Exception> {
|
|
||||||
val request = Request(verb = GET, room = room, server = server, endpoint = "files/$file")
|
|
||||||
return send(request).map { json ->
|
|
||||||
val base64EncodedFile = json["result"] as? String ?: throw Error.ParsingFailed
|
|
||||||
decode(base64EncodedFile) ?: throw Error.ParsingFailed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Sending
|
|
||||||
fun send(message: OpenGroupMessageV2, room: String, server: String): Promise<OpenGroupMessageV2, Exception> {
|
|
||||||
val signedMessage = message.sign() ?: return Promise.ofFail(Error.SigningFailed)
|
|
||||||
val jsonMessage = signedMessage.toJSON()
|
|
||||||
val request = Request(verb = POST, room = room, server = server, endpoint = "messages", parameters = jsonMessage)
|
|
||||||
return send(request).map { json ->
|
|
||||||
@Suppress("UNCHECKED_CAST") val rawMessage = json["message"] as? Map<String, Any>
|
|
||||||
?: throw Error.ParsingFailed
|
|
||||||
val result = OpenGroupMessageV2.fromJSON(rawMessage) ?: throw Error.ParsingFailed
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
storage.addReceivedMessageTimestamp(result.sentTimestamp)
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Messages
|
|
||||||
fun getMessages(room: String, server: String): Promise<List<OpenGroupMessageV2>, Exception> {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
val queryParameters = mutableMapOf<String, String>()
|
|
||||||
storage.getLastMessageServerID(room, server)?.let { lastId ->
|
|
||||||
queryParameters += "from_server_id" to lastId.toString()
|
|
||||||
}
|
|
||||||
val request = Request(verb = GET, room = room, server = server, endpoint = "messages", queryParameters = queryParameters)
|
|
||||||
return send(request).map { json ->
|
|
||||||
@Suppress("UNCHECKED_CAST") val rawMessages = json["messages"] as? List<Map<String, Any>>
|
|
||||||
?: throw Error.ParsingFailed
|
|
||||||
parseMessages(room, server, rawMessages)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseMessages(room: String, server: String, rawMessages: List<Map<*, *>>): List<OpenGroupMessageV2> {
|
|
||||||
val messages = rawMessages.mapNotNull { json ->
|
|
||||||
json as Map<String, Any>
|
|
||||||
try {
|
|
||||||
val message = OpenGroupMessageV2.fromJSON(json) ?: return@mapNotNull null
|
|
||||||
if (message.serverID == null || message.sender.isNullOrEmpty()) return@mapNotNull null
|
|
||||||
val sender = message.sender
|
|
||||||
val data = decode(message.base64EncodedData)
|
|
||||||
val signature = decode(message.base64EncodedSignature)
|
|
||||||
val publicKey = Hex.fromStringCondensed(sender.removing05PrefixIfNeeded())
|
|
||||||
val isValid = curve.verifySignature(publicKey, data, signature)
|
|
||||||
if (!isValid) {
|
|
||||||
Log.d("Loki", "Ignoring message with invalid signature.")
|
|
||||||
return@mapNotNull null
|
|
||||||
}
|
|
||||||
message
|
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return messages
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Message Deletion
|
|
||||||
@JvmStatic
|
|
||||||
fun deleteMessage(serverID: Long, room: String, server: String): Promise<Unit, Exception> {
|
|
||||||
val request = Request(verb = DELETE, room = room, server = server, endpoint = "messages/$serverID")
|
|
||||||
return send(request).map {
|
|
||||||
Log.d("Loki", "Message deletion successful.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDeletedMessages(room: String, server: String): Promise<List<MessageDeletion>, Exception> {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
val queryParameters = mutableMapOf<String, String>()
|
|
||||||
storage.getLastDeletionServerID(room, server)?.let { last ->
|
|
||||||
queryParameters["from_server_id"] = last.toString()
|
|
||||||
}
|
|
||||||
val request = Request(verb = GET, room = room, server = server, endpoint = "deleted_messages", queryParameters = queryParameters)
|
|
||||||
return send(request).map { json ->
|
|
||||||
val type = TypeFactory.defaultInstance().constructCollectionType(List::class.java, MessageDeletion::class.java)
|
|
||||||
val idsAsString = JsonUtil.toJson(json["ids"])
|
|
||||||
val serverIDs = JsonUtil.fromJson<List<MessageDeletion>>(idsAsString, type) ?: throw Error.ParsingFailed
|
|
||||||
val lastMessageServerId = storage.getLastDeletionServerID(room, server) ?: 0
|
|
||||||
val serverID = serverIDs.maxByOrNull {it.id } ?: MessageDeletion.empty
|
|
||||||
if (serverID.id > lastMessageServerId) {
|
|
||||||
storage.setLastDeletionServerID(room, server, serverID.id)
|
|
||||||
}
|
|
||||||
serverIDs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Moderation
|
|
||||||
private fun handleModerators(serverRoomId: String, moderatorList: List<String>) {
|
|
||||||
moderators[serverRoomId] = moderatorList.toMutableSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getModerators(room: String, server: String): Promise<List<String>, Exception> {
|
|
||||||
val request = Request(verb = GET, room = room, server = server, endpoint = "moderators")
|
|
||||||
return send(request).map { json ->
|
|
||||||
@Suppress("UNCHECKED_CAST") val moderatorsJson = json["moderators"] as? List<String>
|
|
||||||
?: throw Error.ParsingFailed
|
|
||||||
val id = "$server.$room"
|
|
||||||
handleModerators(id, moderatorsJson)
|
|
||||||
moderatorsJson
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun ban(publicKey: String, room: String, server: String): Promise<Unit, Exception> {
|
|
||||||
val parameters = mapOf( "public_key" to publicKey )
|
|
||||||
val request = Request(verb = POST, room = room, server = server, endpoint = "block_list", parameters = parameters)
|
|
||||||
return send(request).map {
|
|
||||||
Log.d("Loki", "Banned user: $publicKey from: $server.$room.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun banAndDeleteAll(publicKey: String, room: String, server: String): Promise<Unit, Exception> {
|
|
||||||
val parameters = mapOf( "public_key" to publicKey )
|
|
||||||
val request = Request(verb = POST, room = room, server = server, endpoint = "ban_and_delete_all", parameters = parameters)
|
|
||||||
return send(request).map {
|
|
||||||
Log.d("Loki", "Banned user: $publicKey from: $server.$room.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unban(publicKey: String, room: String, server: String): Promise<Unit, Exception> {
|
|
||||||
val request = Request(verb = DELETE, room = room, server = server, endpoint = "block_list/$publicKey")
|
|
||||||
return send(request).map {
|
|
||||||
Log.d("Loki", "Unbanned user: $publicKey from: $server.$room")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun isUserModerator(publicKey: String, room: String, server: String): Boolean =
|
|
||||||
moderators["$server.$room"]?.contains(publicKey) ?: false
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region General
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
fun compactPoll(rooms: List<String>, server: String): Promise<Map<String, CompactPollResult>, Exception> {
|
|
||||||
val authTokenRequests = rooms.associateWith { room -> getAuthToken(room, server) }
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
val context = MessagingModuleConfiguration.shared.context
|
|
||||||
val timeSinceLastOpen = this.timeSinceLastOpen
|
|
||||||
val useMessageLimit = (hasPerformedInitialPoll[server] != true
|
|
||||||
&& timeSinceLastOpen > OpenGroupPollerV2.maxInactivityPeriod)
|
|
||||||
hasPerformedInitialPoll[server] = true
|
|
||||||
if (!hasUpdatedLastOpenDate) {
|
|
||||||
hasUpdatedLastOpenDate = true
|
|
||||||
TextSecurePreferences.setLastOpenDate(context)
|
|
||||||
}
|
|
||||||
val requests = rooms.mapNotNull { room ->
|
|
||||||
val authToken = try {
|
|
||||||
authTokenRequests[room]?.get()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("Loki", "Failed to get auth token for $room.", e)
|
|
||||||
null
|
|
||||||
} ?: return@mapNotNull null
|
|
||||||
CompactPollRequest(
|
|
||||||
roomID = room,
|
|
||||||
authToken = authToken,
|
|
||||||
fromDeletionServerID = if (useMessageLimit) null else storage.getLastDeletionServerID(room, server),
|
|
||||||
fromMessageServerID = if (useMessageLimit) null else storage.getLastMessageServerID(room, server)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val request = Request(verb = POST, room = null, server = server, endpoint = "compact_poll", isAuthRequired = false, parameters = mapOf( "requests" to requests ))
|
|
||||||
return send(request = request).map { json ->
|
|
||||||
val results = json["results"] as? List<*> ?: throw Error.ParsingFailed
|
|
||||||
results.mapNotNull { json ->
|
|
||||||
if (json !is Map<*,*>) return@mapNotNull null
|
|
||||||
val roomID = json["room_id"] as? String ?: return@mapNotNull null
|
|
||||||
// A 401 means that we didn't provide a (valid) auth token for a route that required one. We use this as an
|
|
||||||
// indication that the token we're using has expired. Note that a 403 has a different meaning; it means that
|
|
||||||
// we provided a valid token but it doesn't have a high enough permission level for the route in question.
|
|
||||||
val statusCode = json["status_code"] as? Int ?: return@mapNotNull null
|
|
||||||
if (statusCode == 401) {
|
|
||||||
// delete auth token and return null
|
|
||||||
storage.removeAuthToken(roomID, server)
|
|
||||||
}
|
|
||||||
// Moderators
|
|
||||||
val moderators = json["moderators"] as? List<String> ?: return@mapNotNull null
|
|
||||||
handleModerators("$server.$roomID", moderators)
|
|
||||||
// Deletions
|
|
||||||
val type = TypeFactory.defaultInstance().constructCollectionType(List::class.java, MessageDeletion::class.java)
|
|
||||||
val idsAsString = JsonUtil.toJson(json["deletions"])
|
|
||||||
val deletions = JsonUtil.fromJson<List<MessageDeletion>>(idsAsString, type) ?: throw Error.ParsingFailed
|
|
||||||
// Messages
|
|
||||||
val rawMessages = json["messages"] as? List<Map<String, Any>> ?: return@mapNotNull null
|
|
||||||
val messages = parseMessages(roomID, server, rawMessages)
|
|
||||||
roomID to CompactPollResult(
|
|
||||||
messages = messages,
|
|
||||||
deletions = deletions,
|
|
||||||
moderators = moderators
|
|
||||||
)
|
|
||||||
}.toMap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDefaultRoomsIfNeeded(): Promise<List<DefaultGroup>, Exception> {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
storage.setOpenGroupPublicKey(defaultServer, defaultServerPublicKey)
|
|
||||||
return getAllRooms(defaultServer).map { groups ->
|
|
||||||
val earlyGroups = groups.map { group ->
|
|
||||||
DefaultGroup(group.id, group.name, null)
|
|
||||||
}
|
|
||||||
// See if we have any cached rooms, and if they already have images don't overwrite them with early non-image results
|
|
||||||
defaultRooms.replayCache.firstOrNull()?.let { replayed ->
|
|
||||||
if (replayed.none { it.image?.isNotEmpty() == true}) {
|
|
||||||
defaultRooms.tryEmit(earlyGroups)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val images = groups.map { group ->
|
|
||||||
group.id to downloadOpenGroupProfilePicture(group.id, defaultServer)
|
|
||||||
}.toMap()
|
|
||||||
groups.map { group ->
|
|
||||||
val image = try {
|
|
||||||
images[group.id]!!.get()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// No image or image failed to download
|
|
||||||
null
|
|
||||||
}
|
|
||||||
DefaultGroup(group.id, group.name, image)
|
|
||||||
}
|
|
||||||
}.success { new ->
|
|
||||||
defaultRooms.tryEmit(new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getInfo(room: String, server: String): Promise<Info, Exception> {
|
|
||||||
val request = Request(verb = GET, room = null, server = server, endpoint = "rooms/$room", isAuthRequired = false)
|
|
||||||
return send(request).map { json ->
|
|
||||||
val rawRoom = json["room"] as? Map<*, *> ?: throw Error.ParsingFailed
|
|
||||||
val id = rawRoom["id"] as? String ?: throw Error.ParsingFailed
|
|
||||||
val name = rawRoom["name"] as? String ?: throw Error.ParsingFailed
|
|
||||||
val imageID = rawRoom["image_id"] as? String
|
|
||||||
Info(id = id, name = name, imageID = imageID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAllRooms(server: String): Promise<List<Info>, Exception> {
|
|
||||||
val request = Request(verb = GET, room = null, server = server, endpoint = "rooms", isAuthRequired = false)
|
|
||||||
return send(request).map { json ->
|
|
||||||
val rawRooms = json["rooms"] as? List<Map<*, *>> ?: throw Error.ParsingFailed
|
|
||||||
rawRooms.mapNotNull {
|
|
||||||
val roomJson = it as? Map<*, *> ?: return@mapNotNull null
|
|
||||||
val id = roomJson["id"] as? String ?: return@mapNotNull null
|
|
||||||
val name = roomJson["name"] as? String ?: return@mapNotNull null
|
|
||||||
val imageID = roomJson["image_id"] as? String
|
|
||||||
Info(id, name, imageID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getMemberCount(room: String, server: String): Promise<Int, Exception> {
|
|
||||||
val request = Request(verb = GET, room = room, server = server, endpoint = "member_count")
|
|
||||||
return send(request).map { json ->
|
|
||||||
val memberCount = json["member_count"] as? Int ?: throw Error.ParsingFailed
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
storage.setUserCount(room, server, memberCount)
|
|
||||||
memberCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
}
|
|
@ -16,7 +16,7 @@ import okhttp3.HttpUrl
|
|||||||
import okhttp3.MediaType
|
import okhttp3.MediaType
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPollerV4.Companion.maxInactivityPeriod
|
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller.Companion.maxInactivityPeriod
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.session.libsession.utilities.AESGCM
|
import org.session.libsession.utilities.AESGCM
|
||||||
@ -39,7 +39,7 @@ import kotlin.collections.component1
|
|||||||
import kotlin.collections.component2
|
import kotlin.collections.component2
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
object OpenGroupApiV4 {
|
object OpenGroupApi {
|
||||||
private val moderators: HashMap<String, Set<String>> =
|
private val moderators: HashMap<String, Set<String>> =
|
||||||
hashMapOf() // Server URL to (channel ID to set of moderator IDs)
|
hashMapOf() // Server URL to (channel ID to set of moderator IDs)
|
||||||
private val curve = Curve25519.getInstance(Curve25519.BEST)
|
private val curve = Curve25519.getInstance(Curve25519.BEST)
|
@ -235,7 +235,7 @@ object MessageSender {
|
|||||||
sentTimestamp = message.sentTimestamp!!,
|
sentTimestamp = message.sentTimestamp!!,
|
||||||
base64EncodedData = Base64.encodeBytes(plaintext),
|
base64EncodedData = Base64.encodeBytes(plaintext),
|
||||||
)
|
)
|
||||||
OpenGroupApiV4.send(openGroupMessage,room,server).success {
|
OpenGroupApi.send(openGroupMessage,room,server).success {
|
||||||
message.openGroupServerMessageID = it.serverID
|
message.openGroupServerMessageID = it.serverID
|
||||||
handleSuccessfulMessageSend(message, destination, openGroupSentTimestamp = it.sentTimestamp)
|
handleSuccessfulMessageSend(message, destination, openGroupSentTimestamp = it.sentTimestamp)
|
||||||
deferred.resolve(Unit)
|
deferred.resolve(Unit)
|
||||||
|
@ -8,7 +8,7 @@ import org.session.libsession.messaging.jobs.GroupAvatarDownloadJob
|
|||||||
import org.session.libsession.messaging.jobs.JobQueue
|
import org.session.libsession.messaging.jobs.JobQueue
|
||||||
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
||||||
import org.session.libsession.messaging.jobs.MessageReceiveParameters
|
import org.session.libsession.messaging.jobs.MessageReceiveParameters
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupApiV4
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupMessageV2
|
import org.session.libsession.messaging.open_groups.OpenGroupMessageV2
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
@ -19,7 +19,7 @@ import java.util.concurrent.ScheduledFuture
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
class OpenGroupPollerV4(private val server: String, private val executorService: ScheduledExecutorService?) {
|
class OpenGroupPoller(private val server: String, private val executorService: ScheduledExecutorService?) {
|
||||||
var hasStarted = false
|
var hasStarted = false
|
||||||
var isCaughtUp = false
|
var isCaughtUp = false
|
||||||
var secondToLastJob: MessageReceiveJob? = null
|
var secondToLastJob: MessageReceiveJob? = null
|
||||||
@ -45,7 +45,7 @@ class OpenGroupPollerV4(private val server: String, private val executorService:
|
|||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val rooms = storage.getAllV2OpenGroups().values.filter { it.server == server }.map { it.room }
|
val rooms = storage.getAllV2OpenGroups().values.filter { it.server == server }.map { it.room }
|
||||||
rooms.forEach { downloadGroupAvatarIfNeeded(it) }
|
rooms.forEach { downloadGroupAvatarIfNeeded(it) }
|
||||||
return OpenGroupApiV4.batch(rooms, server).successBackground { responses ->
|
return OpenGroupApi.batch(rooms, server).successBackground { responses ->
|
||||||
responses.forEach { (room, response) ->
|
responses.forEach { (room, response) ->
|
||||||
val openGroupID = "$server.$room"
|
val openGroupID = "$server.$room"
|
||||||
handleNewMessages(room, openGroupID, response.messages)
|
handleNewMessages(room, openGroupID, response.messages)
|
||||||
@ -55,7 +55,7 @@ class OpenGroupPollerV4(private val server: String, private val executorService:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.always {
|
}.always {
|
||||||
executorService?.schedule(this@OpenGroupPollerV4::poll, pollInterval, TimeUnit.MILLISECONDS)
|
executorService?.schedule(this@OpenGroupPoller::poll, pollInterval, TimeUnit.MILLISECONDS)
|
||||||
}.map { }
|
}.map { }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ class OpenGroupPollerV4(private val server: String, private val executorService:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDeletedMessages(room: String, openGroupID: String, deletions: List<OpenGroupApiV4.MessageDeletion>) {
|
private fun handleDeletedMessages(room: String, openGroupID: String, deletions: List<OpenGroupApi.MessageDeletion>) {
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val dataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
val dataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
||||||
val groupID = GroupUtil.getEncodedOpenGroupID(openGroupID.toByteArray())
|
val groupID = GroupUtil.getEncodedOpenGroupID(openGroupID.toByteArray())
|
@ -1,118 +0,0 @@
|
|||||||
package org.session.libsession.messaging.sending_receiving.pollers
|
|
||||||
|
|
||||||
import nl.komponents.kovenant.Promise
|
|
||||||
import nl.komponents.kovenant.functional.map
|
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
|
||||||
import org.session.libsession.messaging.jobs.*
|
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupMessageV2
|
|
||||||
import org.session.libsession.utilities.Address
|
|
||||||
import org.session.libsession.utilities.GroupUtil
|
|
||||||
import org.session.libsignal.protos.SignalServiceProtos
|
|
||||||
import org.session.libsignal.utilities.successBackground
|
|
||||||
import java.util.concurrent.ScheduledExecutorService
|
|
||||||
import java.util.concurrent.ScheduledFuture
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
class OpenGroupPollerV2(private val server: String, private val executorService: ScheduledExecutorService?) {
|
|
||||||
var hasStarted = false
|
|
||||||
var isCaughtUp = false
|
|
||||||
var secondToLastJob: MessageReceiveJob? = null
|
|
||||||
private var future: ScheduledFuture<*>? = null
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val pollInterval: Long = 4000L
|
|
||||||
const val maxInactivityPeriod = 14 * 24 * 60 * 60 * 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startIfNeeded() {
|
|
||||||
if (hasStarted) { return }
|
|
||||||
hasStarted = true
|
|
||||||
future = executorService?.schedule(::poll, 0, TimeUnit.MILLISECONDS)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stop() {
|
|
||||||
future?.cancel(false)
|
|
||||||
hasStarted = false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun poll(isBackgroundPoll: Boolean = false): Promise<Unit, Exception> {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
val rooms = storage.getAllV2OpenGroups().values.filter { it.server == server }.map { it.room }
|
|
||||||
rooms.forEach { downloadGroupAvatarIfNeeded(it) }
|
|
||||||
return OpenGroupAPIV2.compactPoll(rooms, server).successBackground { responses ->
|
|
||||||
responses.forEach { (room, response) ->
|
|
||||||
val openGroupID = "$server.$room"
|
|
||||||
handleNewMessages(room, openGroupID, response.messages, isBackgroundPoll)
|
|
||||||
handleDeletedMessages(room, openGroupID, response.deletions)
|
|
||||||
if (secondToLastJob == null && !isCaughtUp) {
|
|
||||||
isCaughtUp = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.always {
|
|
||||||
executorService?.schedule(this@OpenGroupPollerV2::poll, pollInterval, TimeUnit.MILLISECONDS)
|
|
||||||
}.map { }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleNewMessages(room: String, openGroupID: String, messages: List<OpenGroupMessageV2>, isBackgroundPoll: Boolean) {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
val groupID = GroupUtil.getEncodedOpenGroupID(openGroupID.toByteArray())
|
|
||||||
// check thread still exists
|
|
||||||
val threadId = storage.getThreadId(Address.fromSerialized(groupID)) ?: -1
|
|
||||||
val threadExists = threadId >= 0
|
|
||||||
if (!hasStarted || !threadExists) { return }
|
|
||||||
val envelopes = messages.sortedBy { it.serverID!! }.map { message ->
|
|
||||||
val senderPublicKey = message.sender!!
|
|
||||||
val builder = SignalServiceProtos.Envelope.newBuilder()
|
|
||||||
builder.type = SignalServiceProtos.Envelope.Type.SESSION_MESSAGE
|
|
||||||
builder.source = senderPublicKey
|
|
||||||
builder.sourceDevice = 1
|
|
||||||
builder.content = message.toProto().toByteString()
|
|
||||||
builder.timestamp = message.sentTimestamp
|
|
||||||
builder.build() to message.serverID
|
|
||||||
}
|
|
||||||
|
|
||||||
envelopes.chunked(256).forEach { list ->
|
|
||||||
val parameters = list.map { (message, serverId) ->
|
|
||||||
MessageReceiveParameters(message.toByteArray(), openGroupMessageServerID = serverId)
|
|
||||||
}
|
|
||||||
JobQueue.shared.add(BatchMessageReceiveJob(parameters, openGroupID))
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentLastMessageServerID = storage.getLastMessageServerID(room, server) ?: 0
|
|
||||||
val actualMax = max(messages.mapNotNull { it.serverID }.maxOrNull() ?: 0, currentLastMessageServerID)
|
|
||||||
if (actualMax > 0) {
|
|
||||||
storage.setLastMessageServerID(room, server, actualMax)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleDeletedMessages(room: String, openGroupID: String, deletions: List<OpenGroupAPIV2.MessageDeletion>) {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
val dataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
|
||||||
val groupID = GroupUtil.getEncodedOpenGroupID(openGroupID.toByteArray())
|
|
||||||
val threadID = storage.getThreadId(Address.fromSerialized(groupID)) ?: return
|
|
||||||
val deletedMessageIDs = deletions.mapNotNull { deletion ->
|
|
||||||
dataProvider.getMessageID(deletion.deletedMessageServerID, threadID)
|
|
||||||
}
|
|
||||||
deletedMessageIDs.forEach { (messageId, isSms) ->
|
|
||||||
MessagingModuleConfiguration.shared.messageDataProvider.deleteMessage(messageId, isSms)
|
|
||||||
}
|
|
||||||
val currentMax = storage.getLastDeletionServerID(room, server) ?: 0L
|
|
||||||
val latestMax = deletions.map { it.id }.maxOrNull() ?: 0L
|
|
||||||
if (latestMax > currentMax && latestMax != 0L) {
|
|
||||||
storage.setLastDeletionServerID(room, server, latestMax)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun downloadGroupAvatarIfNeeded(room: String) {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
if (storage.getGroupAvatarDownloadJob(server, room) != null) return
|
|
||||||
val groupId = GroupUtil.getEncodedOpenGroupID("$server.$room".toByteArray())
|
|
||||||
storage.getGroup(groupId)?.let {
|
|
||||||
if (System.currentTimeMillis() > it.updatedTimestamp + TimeUnit.DAYS.toMillis(7)) {
|
|
||||||
JobQueue.shared.add(GroupAvatarDownloadJob(room, server))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -51,8 +51,6 @@ object SodiumUtilities {
|
|||||||
|
|
||||||
/* Constructs a "blinded" key pair (`ka, kA`) based on an open group server `publicKey` and an ed25519 `keyPair` */
|
/* Constructs a "blinded" key pair (`ka, kA`) based on an open group server `publicKey` and an ed25519 `keyPair` */
|
||||||
fun blindedKeyPair(serverPublicKey: String, edKeyPair: KeyPair): KeyPair? {
|
fun blindedKeyPair(serverPublicKey: String, edKeyPair: KeyPair): KeyPair? {
|
||||||
// if (edKeyPair.publicKey.asBytes.size != publicKeyLength ||
|
|
||||||
// edKeyPair.secretKey.asBytes.size != secretKeyLength) return null
|
|
||||||
val kBytes = generateBlindingFactor(serverPublicKey)
|
val kBytes = generateBlindingFactor(serverPublicKey)
|
||||||
val aBytes = generatePrivateKeyScalar(edKeyPair.secretKey.asBytes)
|
val aBytes = generatePrivateKeyScalar(edKeyPair.secretKey.asBytes)
|
||||||
// Generate the blinded key pair `ka`, `kA`
|
// Generate the blinded key pair `ka`, `kA`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user