// // Copyright (c) 2019 Open Whisper Systems. All rights reserved. // import Foundation import SignalUtilitiesKit let CompareSafetyNumbersActivityType = "org.whispersystems.signal.activity.CompareSafetyNumbers" @objc(OWSCompareSafetyNumbersActivityDelegate) protocol CompareSafetyNumbersActivityDelegate { func compareSafetyNumbersActivitySucceeded(activity: CompareSafetyNumbersActivity) func compareSafetyNumbersActivity(_ activity: CompareSafetyNumbersActivity, failedWithError error: Error) } @objc (OWSCompareSafetyNumbersActivity) class CompareSafetyNumbersActivity: UIActivity { var mySafetyNumbers: String? let delegate: CompareSafetyNumbersActivityDelegate @objc required init(delegate: CompareSafetyNumbersActivityDelegate) { self.delegate = delegate super.init() } // MARK: UIActivity override class var activityCategory: UIActivity.Category { get { return .action } } override var activityType: UIActivity.ActivityType? { get { return UIActivity.ActivityType(rawValue: CompareSafetyNumbersActivityType) } } override var activityTitle: String? { get { return NSLocalizedString("COMPARE_SAFETY_NUMBER_ACTION", comment: "Activity Sheet label") } } override var activityImage: UIImage? { get { return #imageLiteral(resourceName: "ic_lock_outline") } } override func canPerform(withActivityItems activityItems: [Any]) -> Bool { return stringsFrom(activityItems: activityItems).count > 0 } override func prepare(withActivityItems activityItems: [Any]) { let myFormattedSafetyNumbers = stringsFrom(activityItems: activityItems).first mySafetyNumbers = numericOnly(string: myFormattedSafetyNumbers) } override func perform() { defer { activityDidFinish(true) } let pasteboardString = numericOnly(string: UIPasteboard.general.string) guard (pasteboardString != nil && pasteboardString!.count == 60) else { Logger.warn("no valid safety numbers found in pasteboard: \(String(describing: pasteboardString))") let error = OWSErrorWithCodeDescription(OWSErrorCode.userError, NSLocalizedString("PRIVACY_VERIFICATION_FAILED_NO_SAFETY_NUMBERS_IN_CLIPBOARD", comment: "Alert body for user error")) delegate.compareSafetyNumbersActivity(self, failedWithError: error) return } let pasteboardSafetyNumbers = pasteboardString! if pasteboardSafetyNumbers == mySafetyNumbers { Logger.info("successfully matched safety numbers. local numbers: \(String(describing: mySafetyNumbers)) pasteboard:\(pasteboardSafetyNumbers)") delegate.compareSafetyNumbersActivitySucceeded(activity: self) } else { Logger.warn("local numbers: \(String(describing: mySafetyNumbers)) didn't match pasteboard:\(pasteboardSafetyNumbers)") let error = OWSErrorWithCodeDescription(OWSErrorCode.privacyVerificationFailure, NSLocalizedString("PRIVACY_VERIFICATION_FAILED_MISMATCHED_SAFETY_NUMBERS_IN_CLIPBOARD", comment: "Alert body")) delegate.compareSafetyNumbersActivity(self, failedWithError: error) } } // MARK: Helpers func numericOnly(string: String?) -> String? { guard let string = string else { return nil } var numericOnly: String? if let regex = try? NSRegularExpression(pattern: "\\D", options: .caseInsensitive) { numericOnly = regex.stringByReplacingMatches(in: string, options: .withTransparentBounds, range: NSRange(location: 0, length: string.utf16.count), withTemplate: "") } return numericOnly } func stringsFrom(activityItems: [Any]) -> [String] { return activityItems.map { $0 as? String }.filter { $0 != nil }.map { $0! } } }