2020-03-13 17:49:00 +00:00
// F i l e c r e a t e d f r o m S c r e e n T e m p l a t e
// $ c r e a t e S c r e e n . s h V e r i f y K e y V e r i f i c a t i o n V e r i f y B y S c a n n i n g
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License , Version 2.0 ( the " License " ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : // w w w . a p a c h e . o r g / l i c e n s e s / L I C E N S E - 2 . 0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an " AS IS " BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
*/
import Foundation
enum KeyVerificationVerifyByScanningViewModelError : Error {
case unknown
}
final class KeyVerificationVerifyByScanningViewModel : KeyVerificationVerifyByScanningViewModelType {
// MARK: - P r o p e r t i e s
// MARK: P r i v a t e
private let session : MXSession
2020-04-27 17:54:57 +00:00
private let verificationKind : KeyVerificationKind
2020-03-13 17:49:00 +00:00
private let keyVerificationRequest : MXKeyVerificationRequest
private let qrCodeDataCoder : MXQRCodeDataCoder
private let keyVerificationManager : MXKeyVerificationManager
private var qrCodeTransaction : MXQRCodeTransaction ?
private var scannedQRCodeData : MXQRCodeData ?
// MARK: P u b l i c
weak var viewDelegate : KeyVerificationVerifyByScanningViewModelViewDelegate ?
weak var coordinatorDelegate : KeyVerificationVerifyByScanningViewModelCoordinatorDelegate ?
// MARK: - S e t u p
2020-04-27 17:54:57 +00:00
init ( session : MXSession , verificationKind : KeyVerificationKind , keyVerificationRequest : MXKeyVerificationRequest ) {
2020-03-13 17:49:00 +00:00
self . session = session
2020-04-27 17:54:57 +00:00
self . verificationKind = verificationKind
2020-03-13 17:49:00 +00:00
self . keyVerificationManager = self . session . crypto . keyVerificationManager
self . keyVerificationRequest = keyVerificationRequest
self . qrCodeDataCoder = MXQRCodeDataCoder ( )
}
deinit {
2020-03-17 13:06:56 +00:00
self . removePendingQRCodeTransaction ( )
2020-03-13 17:49:00 +00:00
}
// MARK: - P u b l i c
func process ( viewAction : KeyVerificationVerifyByScanningViewAction ) {
switch viewAction {
case . loadData :
self . loadData ( )
case . scannedCode ( payloadData : let payloadData ) :
self . scannedQRCode ( payloadData : payloadData )
case . cannotScan :
self . startSASVerification ( )
case . cancel :
2020-03-27 13:30:58 +00:00
self . cancel ( )
2020-03-13 17:49:00 +00:00
case . acknowledgeMyUserScannedOtherCode :
self . acknowledgeScanOtherCode ( )
}
}
// MARK: - P r i v a t e
private func loadData ( ) {
let qrCodePlayloadData : Data ?
let canShowScanAction : Bool
2020-03-17 13:06:56 +00:00
2020-03-13 17:49:00 +00:00
self . qrCodeTransaction = self . keyVerificationManager . qrCodeTransaction ( withTransactionId : self . keyVerificationRequest . requestId )
if let supportedVerificationMethods = self . keyVerificationRequest . myMethods {
if let qrCodeData = self . qrCodeTransaction ? . qrCodeData {
qrCodePlayloadData = self . qrCodeDataCoder . encode ( qrCodeData )
} else {
qrCodePlayloadData = nil
}
canShowScanAction = self . canShowScanAction ( from : supportedVerificationMethods )
} else {
qrCodePlayloadData = nil
canShowScanAction = false
}
2020-04-27 17:54:57 +00:00
let viewData = KeyVerificationVerifyByScanningViewData ( verificationKind : self . verificationKind ,
qrCodeData : qrCodePlayloadData ,
2020-03-13 17:49:00 +00:00
showScanAction : canShowScanAction )
self . update ( viewState : . loaded ( viewData : viewData ) )
self . registerTransactionDidStateChangeNotification ( )
}
private func canShowScanAction ( from verificationMethods : [ String ] ) -> Bool {
return verificationMethods . contains ( MXKeyVerificationMethodQRCodeScan )
}
2020-03-27 13:30:58 +00:00
private func cancel ( ) {
self . cancelQRCodeTransaction ( )
self . keyVerificationRequest . cancel ( with : MXTransactionCancelCode . user ( ) , success : nil , failure : nil )
2020-04-21 15:00:24 +00:00
self . unregisterTransactionDidStateChangeNotification ( )
2020-03-27 13:30:58 +00:00
self . coordinatorDelegate ? . keyVerificationVerifyByScanningViewModelDidCancel ( self )
}
private func cancelQRCodeTransaction ( ) {
guard let transaction = self . qrCodeTransaction else {
return
}
transaction . cancel ( with : MXTransactionCancelCode . user ( ) )
2020-04-21 15:00:24 +00:00
self . removePendingQRCodeTransaction ( )
2020-03-27 13:30:58 +00:00
}
2020-03-13 17:49:00 +00:00
private func update ( viewState : KeyVerificationVerifyByScanningViewState ) {
self . viewDelegate ? . keyVerificationVerifyByScanningViewModel ( self , didUpdateViewState : viewState )
}
// MARK: Q R c o d e
private func scannedQRCode ( payloadData : Data ) {
self . scannedQRCodeData = self . qrCodeDataCoder . decode ( payloadData )
let isQRCodeValid = self . scannedQRCodeData != nil
self . update ( viewState : . scannedCodeValidated ( isValid : isQRCodeValid ) )
}
private func acknowledgeScanOtherCode ( ) {
guard let scannedQRCodeData = self . scannedQRCodeData else {
return
}
guard let qrCodeTransaction = self . qrCodeTransaction else {
return
}
2020-04-21 15:00:24 +00:00
self . unregisterTransactionDidStateChangeNotification ( )
2020-04-16 16:45:46 +00:00
self . coordinatorDelegate ? . keyVerificationVerifyByScanningViewModel ( self , didScanOtherQRCodeData : scannedQRCodeData , withTransaction : qrCodeTransaction )
2020-04-21 16:57:05 +00:00
}
2020-03-13 17:49:00 +00:00
2020-03-17 13:06:56 +00:00
private func removePendingQRCodeTransaction ( ) {
guard let qrCodeTransaction = self . qrCodeTransaction else {
return
}
self . keyVerificationManager . removeQRCodeTransaction ( withTransactionId : qrCodeTransaction . transactionId )
}
2020-03-13 17:49:00 +00:00
// MARK: S A S
private func startSASVerification ( ) {
self . update ( viewState : . loading )
2020-03-18 16:39:26 +00:00
self . session . crypto . keyVerificationManager . beginKeyVerification ( from : self . keyVerificationRequest , method : MXKeyVerificationMethodSAS , success : { [ weak self ] ( keyVerificationTransaction ) in
2020-03-13 17:49:00 +00:00
guard let self = self else {
return
}
2020-03-17 13:06:56 +00:00
// R e m o v e p e n d i n g Q R c o d e t r a n s a c t i o n , a s w e a r e g o i n g t o u s e S A S v e r i f i c a t i o n
self . removePendingQRCodeTransaction ( )
2020-03-13 17:49:00 +00:00
2020-03-18 16:39:26 +00:00
if keyVerificationTransaction is MXOutgoingSASTransaction = = false {
2020-03-13 17:49:00 +00:00
NSLog ( " [KeyVerificationVerifyByScanningViewModel] SAS transaction should be outgoing " )
self . unregisterTransactionDidStateChangeNotification ( )
self . update ( viewState : . error ( KeyVerificationVerifyByScanningViewModelError . unknown ) )
}
} , failure : { [ weak self ] ( error ) in
guard let self = self else {
return
}
self . update ( viewState : . error ( error ) )
}
)
}
// MARK: - M X K e y V e r i f i c a t i o n T r a n s a c t i o n D i d C h a n g e
private func registerTransactionDidStateChangeNotification ( ) {
NotificationCenter . default . addObserver ( self , selector : #selector ( transactionDidStateChange ( notification : ) ) , name : . MXKeyVerificationTransactionDidChange , object : nil )
}
private func unregisterTransactionDidStateChangeNotification ( ) {
NotificationCenter . default . removeObserver ( self , name : . MXKeyVerificationTransactionDidChange , object : nil )
}
@objc private func transactionDidStateChange ( notification : Notification ) {
guard let transaction = notification . object as ? MXKeyVerificationTransaction else {
return
}
2020-03-26 11:09:17 +00:00
guard self . keyVerificationRequest . requestId = = transaction . transactionId else {
2020-03-24 07:59:06 +00:00
NSLog ( " [KeyVerificationVerifyByScanningViewModel] transactionDidStateChange: Not for our transaction ( \( self . keyVerificationRequest . requestId ) ): \( transaction . transactionId ) " )
2020-03-26 11:09:17 +00:00
return
2020-03-13 17:49:00 +00:00
}
if let sasTransaction = transaction as ? MXSASTransaction {
self . sasTransactionDidStateChange ( sasTransaction )
} else if let qrCodeTransaction = transaction as ? MXQRCodeTransaction {
self . qrCodeTransactionDidStateChange ( qrCodeTransaction )
}
}
private func sasTransactionDidStateChange ( _ transaction : MXSASTransaction ) {
switch transaction . state {
case MXSASTransactionStateShowSAS :
self . unregisterTransactionDidStateChangeNotification ( )
self . coordinatorDelegate ? . keyVerificationVerifyByScanningViewModel ( self , didStartSASVerificationWithTransaction : transaction )
case MXSASTransactionStateCancelled :
guard let reason = transaction . reasonCancelCode else {
return
}
self . unregisterTransactionDidStateChangeNotification ( )
self . update ( viewState : . cancelled ( reason ) )
case MXSASTransactionStateCancelledByMe :
guard let reason = transaction . reasonCancelCode else {
return
}
self . unregisterTransactionDidStateChangeNotification ( )
self . update ( viewState : . cancelledByMe ( reason ) )
default :
break
}
}
private func qrCodeTransactionDidStateChange ( _ transaction : MXQRCodeTransaction ) {
switch transaction . state {
case . verified :
2020-04-16 16:45:46 +00:00
// S h o u l d n o t h a p p e n
2020-03-13 17:49:00 +00:00
self . unregisterTransactionDidStateChangeNotification ( )
2020-04-16 16:45:46 +00:00
self . coordinatorDelegate ? . keyVerificationVerifyByScanningViewModelDidCancel ( self )
2020-03-13 17:49:00 +00:00
case . qrScannedByOther :
2020-04-21 16:57:05 +00:00
self . unregisterTransactionDidStateChangeNotification ( )
self . coordinatorDelegate ? . keyVerificationVerifyByScanningViewModel ( self , qrCodeDidScannedByOtherWithTransaction : transaction )
2020-03-13 17:49:00 +00:00
case . cancelled :
guard let reason = transaction . reasonCancelCode else {
return
}
self . unregisterTransactionDidStateChangeNotification ( )
self . update ( viewState : . cancelled ( reason ) )
case . cancelledByMe :
guard let reason = transaction . reasonCancelCode else {
return
}
self . unregisterTransactionDidStateChangeNotification ( )
self . update ( viewState : . cancelledByMe ( reason ) )
default :
break
}
}
}