pEpForiOS/UI/Settings/Setting/AccountSettings/ViewModel/AccountSettingsViewModel.swift
author Dirk Zimmermann <dz@pep.security>
Thu, 25 Apr 2019 16:25:40 +0200
branchIOS-1542
changeset 8381 907e287ead66
parent 8376 f517a983ae7c
child 8382 855ff0a20d53
permissions -rw-r--r--
IOS-1542 AccountVerificationServiceDelegate not used anymore.
     1 //
     2 //  AccountSettingsViewModel.swift
     3 //  pEpForiOS
     4 //
     5 //  Created by Xavier Algarra on 13/06/2017.
     6 //  Copyright © 2017 p≡p Security S.A. All rights reserved.
     7 //
     8 
     9 import Foundation
    10 import MessageModel
    11 import pEpIOSToolbox
    12 
    13 import PantomimeFramework
    14 
    15 public class AccountSettingsViewModel {
    16     public struct ServerViewModel {
    17         var address: String?
    18         var port: String?
    19         var transport: String?
    20     }
    21 
    22     public struct SecurityViewModel {
    23         var options = Server.Transport.toArray()
    24         var size : Int {
    25             get {
    26                 return options.count
    27             }
    28         }
    29 
    30         subscript(option: Int) -> String {
    31             get {
    32                 return options[option].asString()
    33             }
    34         }
    35     }
    36 
    37     private let headers = [
    38         NSLocalizedString("Account", comment: "Account settings"),
    39         NSLocalizedString("IMAP Settings", comment: "Account settings title IMAP"),
    40         NSLocalizedString("SMTP Settings", comment: "Account settings title SMTP")
    41     ]
    42     private var controlWord = "noRealPassword"
    43 
    44     public let svm = SecurityViewModel()
    45     public let isOAuth2: Bool
    46 
    47     public init(account: Account) {
    48         // We are using a copy of the data here.
    49         // The outside world must not know changed settings until they have been verified.
    50         isOAuth2 = account.server(with: .imap)?.authMethod == AuthMethod.saslXoauth2.rawValue
    51         self.email = account.user.address
    52         self.loginName = account.server(with: .imap)?.credentials.loginName ?? ""
    53         self.name = account.user.userName ?? ""
    54 
    55         if let server = account.imapServer {
    56             self.originalPassword = server.credentials.password
    57             self.imapServer = ServerViewModel(
    58                 address: server.address,
    59                 port: "\(server.port)",
    60                 transport: server.transport?.asString())
    61         } else {
    62             self.imapServer = ServerViewModel()
    63         }
    64 
    65         if let server = account.smtpServer {
    66             self.originalPassword = self.originalPassword ?? server.credentials.password
    67             self.smtpServer = ServerViewModel(
    68                 address: server.address,
    69                 port: "\(server.port)",
    70                 transport: server.transport?.asString())
    71         } else {
    72             self.smtpServer = ServerViewModel()
    73         }
    74     }
    75 
    76     private(set) var email: String
    77 
    78     /// - Note: The email model is based on the assumption that imap.loginName == smtp.loginName
    79     private(set) var loginName: String
    80 
    81     private(set) var name: String
    82 
    83     private(set) var smtpServer: ServerViewModel
    84 
    85     private(set) var imapServer: ServerViewModel
    86 
    87     weak var delegate: AccountVerificationResultDelegate?
    88 
    89     /// Holding both the data of the current account in verification,
    90     /// and also the implementation of the verification.
    91     private var verifiableAccount: VerifiableAccountProtocol?
    92 
    93     /// If the credentials have either an IMAP or SMTP password,
    94     /// it gets stored here.
    95     private var originalPassword: String?
    96 
    97     /// If there was OAUTH2 for this account, here is a current token.
    98     /// This trumps both the `originalPassword` and a password given by the user
    99     /// via the UI.
   100     private var accessToken: OAuth2AccessTokenProtocol?
   101 
   102     // Currently we assume imap and smtp servers exist already (update).
   103     // If we run into problems here modify to updateOrCreate.
   104     func update(loginName: String, name: String, password: String? = nil, imap: ServerViewModel,
   105                 smtp: ServerViewModel) {
   106         var theVerifier = verifiableAccount ?? VerifiableAccount()
   107         verifiableAccount = theVerifier
   108 
   109         theVerifier.address = email
   110         theVerifier.userName = name
   111 
   112         if loginName != email {
   113             theVerifier.loginName = loginName
   114         }
   115 
   116         if isOAuth2 {
   117             if self.accessToken == nil {
   118                 Logger.frontendLogger.errorAndCrash("Have to do OAUTH2, but lacking current token")
   119             }
   120             theVerifier.authMethod = .saslXoauth2
   121             theVerifier.accessToken = accessToken
   122             // OAUTH2 trumps any password
   123             theVerifier.password = nil
   124         } else {
   125             theVerifier.password = originalPassword
   126             if password != nil {
   127                 theVerifier.password = password
   128             }
   129         }
   130 
   131         // IMAP
   132         theVerifier.serverIMAP = imap.address
   133         if let portString = imap.port, let port = UInt16(portString) {
   134             theVerifier.portIMAP = port
   135         }
   136         if let transport = Server.Transport(fromString: imap.transport) {
   137             theVerifier.transportIMAP = ConnectionTransport.init(transport: transport)
   138         }
   139 
   140         // SMTP
   141         theVerifier.serverSMTP = smtp.address
   142         if let portString = smtp.port, let port = UInt16(portString) {
   143             theVerifier.portSMTP = port
   144         }
   145         if let transport = Server.Transport(fromString: smtp.transport) {
   146             theVerifier.transportSMTP = ConnectionTransport.init(transport: transport)
   147         }
   148 
   149         theVerifier.verifiableAccountDelegate = self
   150 
   151         do {
   152             try theVerifier.verify()
   153         } catch {
   154             delegate?.didVerify(result: .noImapConnectData, accountInput: theVerifier)
   155         }
   156     }
   157 
   158     func sectionIsValid(section: Int) -> Bool {
   159         return section >= 0 && section < headers.count
   160     }
   161 
   162     var count: Int {
   163         get {
   164             return headers.count
   165         }
   166     }
   167 
   168     subscript(section: Int) -> String {
   169         get {
   170             assert(sectionIsValid(section: section), "Section out of range")
   171             return headers[section]
   172         }
   173     }
   174 
   175     private func server(from viewModel:ServerViewModel, serverType:Server.ServerType,
   176                         loginName: String, password: String?, key: String? = nil) -> Server? {
   177         guard let viewModelPort = viewModel.port,
   178             let port = UInt16(viewModelPort),
   179             let address = viewModel.address else {
   180                 Logger.frontendLogger.errorAndCrash("viewModel misses required data.")
   181                 return nil
   182         }
   183         let transport = Server.Transport(fromString: viewModel.transport)
   184 
   185         let credentials = ServerCredentials.create(loginName: loginName, key: key)
   186         if password != nil && password != "" {
   187             credentials.password = password
   188         }
   189 
   190         let server = Server.create(serverType: serverType, port: port, address: address,
   191                                    transport: transport, credentials: credentials)
   192 
   193         return server
   194     }
   195 
   196     func updateToken(accessToken: OAuth2AccessTokenProtocol) {
   197         self.accessToken = accessToken
   198     }
   199 }
   200 
   201 // MARK: - VerifiableAccountDelegate
   202 
   203 extension AccountSettingsViewModel: VerifiableAccountDelegate {
   204     public func didEndVerification(result: Result<Void, Error>) {
   205         switch result {
   206         case .success(()):
   207             do {
   208                 try verifiableAccount?.save()
   209             } catch {
   210                 Logger.frontendLogger.log(error: error)
   211                 Logger.frontendLogger.errorAndCrash("Unexpected error on saving the account")
   212             }
   213         case .failure(let error):
   214             if let imapError = error as? ImapSyncError {
   215                 delegate?.didVerify(
   216                     result: .imapError(imapError), accountInput: verifiableAccount)
   217             } else if let smtpError = error as? SmtpSendError {
   218                 delegate?.didVerify(
   219                     result: .smtpError(smtpError), accountInput: verifiableAccount)
   220             } else {
   221                 Logger.frontendLogger.log(error: error)
   222                 Logger.frontendLogger.errorAndCrash("Unexpected error")
   223             }
   224         }
   225     }
   226 }