Ensure identity records are good before trying to send media.

fork-5.53.8
Alex Hart 2022-03-14 16:13:21 -03:00 zatwierdzone przez Cody Henthorne
rodzic 5b91c927b6
commit b0458f10a3
7 zmienionych plików z 90 dodań i 58 usunięć

Wyświetl plik

@ -1,16 +1,11 @@
package org.thoughtcrime.securesms.conversation.mutiselect.forward package org.thoughtcrime.securesms.conversation.mutiselect.forward
import android.content.Context import android.content.Context
import androidx.core.util.Consumer
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
import org.thoughtcrime.securesms.contacts.paged.RecipientSearchKey
import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.identity.IdentityRecordList
import org.thoughtcrime.securesms.database.model.IdentityRecord
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sharing.MultiShareArgs import org.thoughtcrime.securesms.sharing.MultiShareArgs
@ -20,25 +15,12 @@ import org.whispersystems.libsignal.util.guava.Optional
class MultiselectForwardRepository(context: Context) { class MultiselectForwardRepository(context: Context) {
private val context = context.applicationContext
class MultiselectForwardResultHandlers( class MultiselectForwardResultHandlers(
val onAllMessageSentSuccessfully: () -> Unit, val onAllMessageSentSuccessfully: () -> Unit,
val onSomeMessagesFailed: () -> Unit, val onSomeMessagesFailed: () -> Unit,
val onAllMessagesFailed: () -> Unit val onAllMessagesFailed: () -> Unit
) )
fun checkForBadIdentityRecords(contactSearchKeys: Set<ContactSearchKey>, consumer: Consumer<List<IdentityRecord>>) {
SignalExecutors.BOUNDED.execute {
val recipients: List<Recipient> = contactSearchKeys
.filterIsInstance<RecipientSearchKey>()
.map { Recipient.resolved(it.recipientId) }
val identityRecordList: IdentityRecordList = ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecords(recipients)
consumer.accept(identityRecordList.untrustedRecords)
}
}
fun canSelectRecipient(recipientId: Optional<RecipientId>): Single<Boolean> { fun canSelectRecipient(recipientId: Optional<RecipientId>): Single<Boolean> {
if (!recipientId.isPresent) { if (!recipientId.isPresent) {
return Single.just(true) return Single.just(true)

Wyświetl plik

@ -4,7 +4,9 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
import org.thoughtcrime.securesms.contacts.paged.RecipientSearchKey
import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mediasend.v2.UntrustedRecords
import org.thoughtcrime.securesms.sharing.MultiShareArgs import org.thoughtcrime.securesms.sharing.MultiShareArgs
import org.thoughtcrime.securesms.util.livedata.Store import org.thoughtcrime.securesms.util.livedata.Store
@ -23,7 +25,7 @@ class MultiselectForwardViewModel(
store.update { it.copy(stage = MultiselectForwardState.Stage.FirstConfirmation) } store.update { it.copy(stage = MultiselectForwardState.Stage.FirstConfirmation) }
} else { } else {
store.update { it.copy(stage = MultiselectForwardState.Stage.LoadingIdentities) } store.update { it.copy(stage = MultiselectForwardState.Stage.LoadingIdentities) }
repository.checkForBadIdentityRecords(selectedContacts) { identityRecords -> UntrustedRecords.checkForBadIdentityRecords(selectedContacts.filterIsInstance(RecipientSearchKey::class.java).toSet()) { identityRecords ->
if (identityRecords.isEmpty()) { if (identityRecords.isEmpty()) {
performSend(additionalMessage, selectedContacts) performSend(additionalMessage, selectedContacts)
} else { } else {

Wyświetl plik

@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.components.emoji.EmojiEventListener
import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration
import org.thoughtcrime.securesms.contacts.paged.ContactSearchState import org.thoughtcrime.securesms.contacts.paged.ContactSearchState
import org.thoughtcrime.securesms.conversation.mutiselect.forward.SearchConfigurationProvider import org.thoughtcrime.securesms.conversation.mutiselect.forward.SearchConfigurationProvider
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog
import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment
import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment
import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mediasend.Media
@ -146,6 +147,10 @@ class MediaSelectionActivity :
} }
override fun onSendError(error: Throwable) { override fun onSendError(error: Throwable) {
if (error is UntrustedRecords.UntrustedRecordsException) {
Log.w(TAG, "Send failed due to untrusted identities.")
SafetyNumberChangeDialog.show(supportFragmentManager, error.untrustedRecords)
} else {
setResult(RESULT_CANCELED) setResult(RESULT_CANCELED)
// TODO [alex] - Toast // TODO [alex] - Toast
@ -154,6 +159,7 @@ class MediaSelectionActivity :
finish() finish()
overridePendingTransition(R.anim.stationary, R.anim.camera_slide_to_bottom) overridePendingTransition(R.anim.stationary, R.anim.camera_slide_to_bottom)
} }
}
override fun onNoMediaSelected() { override fun onNoMediaSelected() {
Log.w(TAG, "No media selected. Exiting.") Log.w(TAG, "No media selected. Exiting.")

Wyświetl plik

@ -280,7 +280,8 @@ class MediaSelectionViewModel(
fun send( fun send(
selectedContacts: List<RecipientSearchKey> = emptyList() selectedContacts: List<RecipientSearchKey> = emptyList()
): Maybe<MediaSendActivityResult> { ): Maybe<MediaSendActivityResult> {
return repository.send( return UntrustedRecords.checkForBadIdentityRecords(selectedContacts.toSet()).andThen(
repository.send(
store.state.selectedMedia, store.state.selectedMedia,
store.state.editorStateMap, store.state.editorStateMap,
store.state.quality, store.state.quality,
@ -288,10 +289,11 @@ class MediaSelectionViewModel(
store.state.transportOption.isSms, store.state.transportOption.isSms,
isViewOnceEnabled(), isViewOnceEnabled(),
destination.getRecipientSearchKey(), destination.getRecipientSearchKey(),
if (selectedContacts.isNotEmpty()) selectedContacts else destination.getRecipientSearchKeyList(), selectedContacts.ifEmpty { destination.getRecipientSearchKeyList() },
MentionAnnotation.getMentionsFromAnnotations(store.state.message), MentionAnnotation.getMentionsFromAnnotations(store.state.message),
store.state.transportOption store.state.transportOption
) )
)
} }
private fun isViewOnceEnabled(): Boolean { private fun isViewOnceEnabled(): Boolean {

Wyświetl plik

@ -0,0 +1,46 @@
package org.thoughtcrime.securesms.mediasend.v2
import androidx.core.util.Consumer
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.contacts.paged.RecipientSearchKey
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.IdentityRecord
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.recipients.Recipient
object UntrustedRecords {
fun checkForBadIdentityRecords(contactSearchKeys: Set<RecipientSearchKey>): Completable {
return Completable.fromAction {
val untrustedRecords: List<IdentityRecord> = checkForBadIdentityRecordsSync(contactSearchKeys)
if (untrustedRecords.isNotEmpty()) {
throw UntrustedRecordsException(untrustedRecords)
}
}.subscribeOn(Schedulers.io())
}
fun checkForBadIdentityRecords(contactSearchKeys: Set<RecipientSearchKey>, consumer: Consumer<List<IdentityRecord>>) {
SignalExecutors.BOUNDED.execute {
consumer.accept(checkForBadIdentityRecordsSync(contactSearchKeys))
}
}
private fun checkForBadIdentityRecordsSync(contactSearchKeys: Set<RecipientSearchKey>): List<IdentityRecord> {
val recipients: List<Recipient> = contactSearchKeys
.map { Recipient.resolved(it.recipientId) }
.map { recipient ->
when {
recipient.isGroup -> recipient.participants
recipient.isDistributionList -> Recipient.resolvedList(SignalDatabase.distributionLists.getMembers(recipient.distributionListId.get()))
else -> listOf(recipient)
}
}
.flatten()
return ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecords(recipients).untrustedRecords
}
class UntrustedRecordsException(val untrustedRecords: List<IdentityRecord>) : Throwable()
}

Wyświetl plik

@ -2,19 +2,17 @@ package org.thoughtcrime.securesms.mediasend.v2.text.send
import android.content.Context import android.content.Context
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.ThreadUtil import org.signal.core.util.ThreadUtil
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
import org.thoughtcrime.securesms.contacts.paged.RecipientSearchKey import org.thoughtcrime.securesms.contacts.paged.RecipientSearchKey
import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.identity.IdentityRecordList
import org.thoughtcrime.securesms.database.model.StoryType import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.fonts.TextFont import org.thoughtcrime.securesms.fonts.TextFont
import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.linkpreview.LinkPreview import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.mediasend.v2.UntrustedRecords
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryPostCreationState import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryPostCreationState
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage import org.thoughtcrime.securesms.mms.OutgoingMediaMessage
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage
@ -35,7 +33,17 @@ class TextStoryPostSendRepository(context: Context) {
} }
fun send(contactSearchKey: Set<ContactSearchKey>, textStoryPostCreationState: TextStoryPostCreationState, linkPreview: LinkPreview?): Single<TextStoryPostSendResult> { fun send(contactSearchKey: Set<ContactSearchKey>, textStoryPostCreationState: TextStoryPostCreationState, linkPreview: LinkPreview?): Single<TextStoryPostSendResult> {
return checkForBadIdentityRecords(contactSearchKey).flatMap { result -> return UntrustedRecords
.checkForBadIdentityRecords(contactSearchKey.filterIsInstance(RecipientSearchKey::class.java).toSet())
.toSingleDefault<TextStoryPostSendResult>(TextStoryPostSendResult.Success)
.onErrorReturn {
if (it is UntrustedRecords.UntrustedRecordsException) {
TextStoryPostSendResult.UntrustedRecordsError(it.untrustedRecords)
} else {
TextStoryPostSendResult.Failure
}
}
.flatMap { result ->
if (result is TextStoryPostSendResult.Success) { if (result is TextStoryPostSendResult.Success) {
performSend(contactSearchKey, textStoryPostCreationState, linkPreview) performSend(contactSearchKey, textStoryPostCreationState, linkPreview)
} else { } else {
@ -44,21 +52,6 @@ class TextStoryPostSendRepository(context: Context) {
} }
} }
private fun checkForBadIdentityRecords(contactSearchKeys: Set<ContactSearchKey>): Single<TextStoryPostSendResult> {
return Single.fromCallable {
val recipients: List<Recipient> = contactSearchKeys
.filterIsInstance<RecipientSearchKey>()
.map { Recipient.resolved(it.recipientId) }
val identityRecordList: IdentityRecordList = ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecords(recipients)
if (identityRecordList.untrustedRecords.isNotEmpty()) {
TextStoryPostSendResult.UntrustedRecordsError(identityRecordList.untrustedRecords)
} else {
TextStoryPostSendResult.Success
}
}.subscribeOn(Schedulers.io())
}
private fun performSend(contactSearchKey: Set<ContactSearchKey>, textStoryPostCreationState: TextStoryPostCreationState, linkPreview: LinkPreview?): Single<TextStoryPostSendResult> { private fun performSend(contactSearchKey: Set<ContactSearchKey>, textStoryPostCreationState: TextStoryPostCreationState, linkPreview: LinkPreview?): Single<TextStoryPostSendResult> {
return Single.fromCallable { return Single.fromCallable {
val messages: MutableList<OutgoingSecureMediaMessage> = mutableListOf() val messages: MutableList<OutgoingSecureMediaMessage> = mutableListOf()

Wyświetl plik

@ -4,5 +4,6 @@ import org.thoughtcrime.securesms.database.model.IdentityRecord
sealed class TextStoryPostSendResult { sealed class TextStoryPostSendResult {
object Success : TextStoryPostSendResult() object Success : TextStoryPostSendResult()
object Failure : TextStoryPostSendResult()
data class UntrustedRecordsError(val untrustedRecords: List<IdentityRecord>) : TextStoryPostSendResult() data class UntrustedRecordsError(val untrustedRecords: List<IdentityRecord>) : TextStoryPostSendResult()
} }