1616// along with this program. If not, see http://www.gnu.org/licenses/.
1717//
1818
19+ import WireCoreCrypto
1920import WireDataModel
2021import WireLogging
2122
@@ -398,7 +399,7 @@ public final class MessageSender: MessageSenderInterface {
398399 try await mlsService. reEstablishPendingGroup ( groupID: groupID)
399400 }
400401
401- try await mlsService. commitPendingProposals ( in: groupID)
402+ try await mlsService. commitPendingProposals ( in: groupID, skipRetry : true )
402403 let encryptedData = try await encryptMlsMessage ( message, groupID: groupID)
403404
404405 // set expiration so request can be expired later
@@ -423,51 +424,84 @@ public final class MessageSender: MessageSenderInterface {
423424 message. delivered ( with: response)
424425 }
425426 } catch let error as SendMLSMessageFailure {
426- switch error {
427- case . mlsStaleMessage:
428- // We should try to repair the conversation for the `mlsStaleMessage` error.
429- // This error indicates that the message was not encrypted in the latest epoch.
430- let operation : ( ) async throws -> Void = { [ weak self] in
431- try await self ? . sendMessage ( message: message)
432- }
433427
434- try await handleMLSStaleMessageError (
428+ try await handleSendMLSMessageFailure ( error, message: message, groupID: groupID, mlsService: mlsService)
429+ } catch let CoreCryptoError . Mls( . MessageRejected( reason: reason) ) {
430+
431+ if let supportedError = SendMLSMessageFailure ( from: reason) {
432+ try await handleSendMLSMessageFailure (
433+ supportedError,
434+ message: message,
435435 groupID: groupID,
436- mlsService: mlsService,
437- operation: operation
436+ mlsService: mlsService
438437 )
439- case . mlsInvalidLeafNodeIndex, . mlsInvalidLeafNodeSignature:
440- let feature = await featureRepository. fetchAllowedGlobalOperations ( )
441- guard feature. status == . enabled,
442- feature. config. mlsConversationReset == true
443- else {
444- WireLogger . messaging. debug (
445- " No need to initiate reset broken MLS conversation, FF is OFF "
446- )
447- throw error
448- }
438+ } else {
439+ throw CoreCryptoError . Mls ( . MessageRejected( reason: reason) )
440+ }
449441
450- let epoch = await context. perform { message. conversation? . epoch }
442+ }
443+ }
451444
452- await initiateResetMLSConversationUseCase
453- . invoke (
454- groupID: groupID,
455- epoch: epoch ?? 0
456- )
457- case let . groupOutOfSync( missingUsers) :
458- guard retryCount < maxRetryAttempts else {
459- retryCount = 0
460- throw error
461- }
445+ private func handleSendMLSMessageFailure(
446+ _ error: SendMLSMessageFailure ,
447+ message: any SendableMessage ,
448+ groupID: MLSGroupID ,
449+ mlsService: MLSServiceInterface
450+ ) async throws {
451+ switch error {
452+ case . mlsStaleMessage:
453+ // We should try to repair the conversation for the `mlsStaleMessage` error.
454+ // This error indicates that the message was not encrypted in the latest epoch.
455+ let operation : ( ) async throws -> Void = { [ weak self] in
456+ try await self ? . sendMessage ( message: message)
457+ }
462458
463- retryCount += 1
459+ try await handleMLSStaleMessageError (
460+ groupID: groupID,
461+ mlsService: mlsService,
462+ operation: operation
463+ )
464+ case . mlsInvalidLeafNodeIndex, . mlsInvalidLeafNodeSignature:
465+ let feature = await featureRepository. fetchAllowedGlobalOperations ( )
466+ guard feature. status == . enabled,
467+ feature. config. mlsConversationReset == true
468+ else {
469+ WireLogger . messaging. debug (
470+ " No need to initiate reset broken MLS conversation, FF is OFF "
471+ )
472+ throw error
473+ }
474+
475+ let epoch = await context. perform { message. conversation? . epoch }
464476
465- let users = missingUsers. map { MLSUser ( $0) }
466- try await mlsService. addMembersToConversation ( with: users, for: groupID)
467- try await sendMessage ( message: message)
468- default :
477+ await initiateResetMLSConversationUseCase
478+ . invoke (
479+ groupID: groupID,
480+ epoch: epoch ?? 0
481+ )
482+ case let . groupOutOfSync( missingUsers) :
483+ guard retryCount < maxRetryAttempts else {
484+ retryCount = 0
469485 throw error
470486 }
487+
488+ retryCount += 1
489+
490+ let users = missingUsers. map { MLSUser ( $0) }
491+ try await mlsService. addMembersToConversation ( with: users, for: groupID)
492+ try await sendMessage ( message: message)
493+ case . mlsCommitMissingReferences, . mlsClientMismatch:
494+ // here a simple retry is used but as an optim we could use a backoff
495+ guard retryCount < maxRetryAttempts else {
496+ retryCount = 0
497+ throw error
498+ }
499+
500+ retryCount += 1
501+
502+ try await sendMessage ( message: message)
503+ default :
504+ throw error
471505 }
472506 }
473507
@@ -532,3 +566,32 @@ private extension Payload.ClientListByQualifiedUserID {
532566 }
533567
534568}
569+
570+ private extension SendMLSMessageFailure {
571+
572+ init ? ( from reason: String ) {
573+ guard let error = try ? JSONDecoder ( ) . decode (
574+ MLSTransportError . self,
575+ from: Data ( reason. utf8)
576+ ) else {
577+ return nil
578+ }
579+
580+ switch error {
581+ case . mlsClientMismatch:
582+ self = . mlsClientMismatch
583+ case . mlsCommitMissingReferences:
584+ self = . mlsCommitMissingReferences( message: " " )
585+ case . mlsStaleMessage:
586+ self = . mlsStaleMessage
587+ case . mlsInvalidLeafNodeIndex:
588+ self = . mlsInvalidLeafNodeIndex( message: " " )
589+ case . mlsInvalidLeafNodeSignature:
590+ self = . mlsInvalidLeafNodeSignature( message: " " )
591+ case let . groupOutOfSync( missingUsers) :
592+ self = . groupOutOfSync( missingUsers: missingUsers)
593+ default :
594+ return nil
595+ }
596+ }
597+ }
0 commit comments