Commit 239dcd61 authored by Martin's avatar Martin
Browse files

Merge branch 'IOS-2885' into 1.1.260

parents bfd49312 7044e2f1
......@@ -42,6 +42,8 @@ public class VerifiableAccount: VerifiableAccountProtocol {
/// Someone who tells us whether or not to create a pEp folder for storing sync messages for
/// synced accounts.
private let usePEPFolderProvider: UsePEPFolderProviderProtocol?
public var originalImapPassword: String?
public var originalSmtpPassword: String?
// MARK: - VerifiableAccountProtocol (delegate)
......@@ -53,7 +55,8 @@ public class VerifiableAccount: VerifiableAccountProtocol {
public var address: String?
public var userName: String?
public var authMethod: AuthMethod?
public var password: String?
public var imapPassword: String?
public var smtpPassword: String?
public var keySyncEnable: Bool
public var accessToken: OAuth2AccessTokenProtocol?
public var clientCertificate: ClientCertificate?
......@@ -75,7 +78,8 @@ public class VerifiableAccount: VerifiableAccountProtocol {
address: String? = nil,
userName: String? = nil,
authMethod: AuthMethod? = nil,
password: String? = nil,
imapPassword: String? = nil,
smtpPassword: String? = nil,
accessToken: OAuth2AccessTokenProtocol? = nil,
loginNameIMAP: String? = nil,
serverIMAP: String? = nil,
......@@ -89,12 +93,15 @@ public class VerifiableAccount: VerifiableAccountProtocol {
manuallyTrustedImapServer: Bool = false,
keySyncEnable: Bool = true,
containsCompleteServerInfo: Bool = false,
usePEPFolderProvider: UsePEPFolderProviderProtocol? = nil) {
usePEPFolderProvider: UsePEPFolderProviderProtocol? = nil,
originalImapPassword: String? = nil,
originalSmtpPassword: String? = nil) {
self.verifiableAccountDelegate = verifiableAccountDelegate
self.address = address
self.userName = userName
self.authMethod = authMethod
self.password = password
self.imapPassword = imapPassword
self.smtpPassword = smtpPassword
self.accessToken = accessToken
self.loginNameIMAP = loginNameIMAP
self.serverIMAP = serverIMAP
......@@ -109,6 +116,8 @@ public class VerifiableAccount: VerifiableAccountProtocol {
self.keySyncEnable = keySyncEnable
self.containsCompleteServerInfo = containsCompleteServerInfo
self.usePEPFolderProvider = usePEPFolderProvider
self.originalImapPassword = originalImapPassword
self.originalSmtpPassword = originalSmtpPassword
}
// MARK: - VerifiableAccountProtocol (behaviour)
......@@ -198,13 +207,33 @@ extension VerifiableAccount {
switch theImapResult {
case .failure(let error):
resetPasswordsInKeychain()
verifiableAccountDelegate?.didEndVerification(result: .failure(error))
case .success(()):
switch theSmtpResult {
case .failure(let error):
resetPasswordsInKeychain()
verifiableAccountDelegate?.didEndVerification(result: .failure(error))
case .success(()):
self.verifiableAccountDelegate?.didEndVerification(result: .success(()))
verifiableAccountDelegate?.didEndVerification(result: .success(()))
}
}
}
// Set the original passwords again to save it in Key Chain.
// This prevents to have a failing password stored because of the account verification.
private func resetPasswordsInKeychain() {
let context = Stack.shared.newPrivateConcurrentContext
context.performAndWait {
if let address = address,
let cdAccount = CdAccount.by(address: address, context: context) {
let account = cdAccount.account()
if let originalPassword = originalImapPassword {
account.imapServer?.credentials.password = originalPassword
}
if let originalPassword = originalSmtpPassword {
account.smtpServer?.credentials.password = originalPassword
}
}
}
}
......@@ -214,8 +243,8 @@ extension VerifiableAccount {
extension VerifiableAccount {
private var isValidPassword: Bool {
if let pass = password {
return pass.count > 0
if let imapPass = imapPassword, let smtpPass = smtpPassword {
return imapPass.count > 0 && smtpPass.count > 0
}
return false
}
......@@ -226,8 +255,8 @@ extension VerifiableAccount {
(authMethod == .saslXoauth2 || (loginNameIMAP?.count ?? 0) >= 1) &&
(authMethod == .saslXoauth2 || (loginNameSMTP?.count ?? 0) >= 1) &&
(address?.count ?? 0) > 0 &&
((authMethod == .saslXoauth2 && accessToken != nil && password == nil) ||
(accessToken == nil && password != nil)) &&
((authMethod == .saslXoauth2 && accessToken != nil && imapPassword == nil && smtpPassword == nil) ||
(accessToken == nil && imapPassword != nil && smtpPassword != nil)) &&
portIMAP > 0 &&
portSMTP > 0 &&
(serverIMAP?.count ?? 0) > 0 &&
......@@ -364,7 +393,7 @@ extension VerifiableAccount {
credentials: theImapServer.credentials ?? CdServerCredentials(context: moc),
loginName: me.loginNameIMAP,
address: me.address,
password: me.password,
password: me.imapPassword,
clientCertificate: cdClientCertificate,
accessToken: me.accessToken)
credentialsImap.servers = NSSet(array: [theImapServer])
......@@ -374,7 +403,7 @@ extension VerifiableAccount {
credentials: theSmtpServer.credentials ?? CdServerCredentials(context: moc),
loginName: me.loginNameSMTP,
address: me.address,
password: me.password,
password: me.smtpPassword,
clientCertificate: cdClientCertificate,
accessToken: me.accessToken)
credentialsSmtp.servers = NSSet(array: [theSmtpServer])
......@@ -461,7 +490,6 @@ extension VerifiableAccount {
extension VerifiableAccount: VerifiableAccountIMAPDelegate {
func verified(verifier: VerifiableAccountIMAP,
result: Result<Void, Error>) {
verifier.delegate = nil
syncQueue.async { [weak self] in
self?.imapResult = result
self?.checkSuccess()
......@@ -474,7 +502,6 @@ extension VerifiableAccount: VerifiableAccountIMAPDelegate {
extension VerifiableAccount: VerifiableAccountSMTPDelegate {
func verified(verifier: VerifiableAccountSMTP,
result: Result<Void, Error>) {
verifier.delegate = nil
syncQueue.async { [weak self] in
self?.smtpResult = result
self?.checkSuccess()
......@@ -491,12 +518,15 @@ extension VerifiableAccount {
/// to find out if server data is still missing or not.
/// - Parameter type: The account type
public static func verifiableAccount(for type: AccountType,
usePEPFolderProvider: UsePEPFolderProviderProtocol? = nil) -> VerifiableAccountProtocol {
usePEPFolderProvider: UsePEPFolderProviderProtocol? = nil,
originalImapPassword: String? = nil,
originalSmtpPassword: String? = nil) -> VerifiableAccountProtocol {
var account = VerifiableAccount(verifiableAccountDelegate: nil,
address: nil,
userName: nil,
authMethod: .cramMD5,
password: nil,
imapPassword: nil,
smtpPassword: nil,
accessToken: nil,
loginNameIMAP: nil,
serverIMAP: nil,
......@@ -510,14 +540,18 @@ extension VerifiableAccount {
manuallyTrustedImapServer: false,
keySyncEnable: true,
containsCompleteServerInfo: false,
usePEPFolderProvider: usePEPFolderProvider)
usePEPFolderProvider: usePEPFolderProvider,
originalImapPassword: originalImapPassword,
originalSmtpPassword: originalSmtpPassword)
switch type {
case .gmail:
account = VerifiableAccount(verifiableAccountDelegate: nil,
address: nil,
userName: nil,
authMethod: .saslXoauth2,
password: nil,
imapPassword: nil,
smtpPassword: nil,
accessToken: nil,
loginNameIMAP: nil,
serverIMAP: "imap.gmail.com",
......@@ -537,7 +571,8 @@ extension VerifiableAccount {
address: nil,
userName: nil,
authMethod: .cramMD5,
password: nil,
imapPassword: nil,
smtpPassword: nil,
accessToken: nil,
loginNameIMAP: nil,
serverIMAP: "outlook.office365.com",
......@@ -557,7 +592,8 @@ extension VerifiableAccount {
address: nil,
userName: nil,
authMethod: .cramMD5,
password: nil,
imapPassword: nil,
smtpPassword: nil,
accessToken: nil,
loginNameIMAP: nil,
serverIMAP: "imap.mail.me.com",
......@@ -577,7 +613,8 @@ extension VerifiableAccount {
address: nil,
userName: nil,
authMethod: .cramMD5,
password: nil,
imapPassword: nil,
smtpPassword: nil,
accessToken: nil,
loginNameIMAP: nil,
serverIMAP: "outlook.office365.com",
......
......@@ -31,7 +31,7 @@ extension VerifiableAccountValidationError: LocalizedError {
}
/// The delegate used for the `VerifiableAccountProtocol`.
public protocol VerifiableAccountDelegate: class {
public protocol VerifiableAccountDelegate: AnyObject {
/// Gets called once the verification has finished, successfully or not.
/// The given `result` indicates success or failure.
func didEndVerification(result: Result<Void, Error>)
......@@ -74,8 +74,12 @@ public protocol VerifiableAccountProtocol {
var authMethod: AuthMethod? { get set }
/// The password, if needed, to log in.
/// Valid for both IMAP and SMTP servers.
var password: String? { get set }
/// Valid for IMAP servers.
var imapPassword: String? { get set }
/// The password, if needed, to log in.
/// Valid for SMTP servers.
var smtpPassword: String? { get set }
/// Enable/disale keySync to this account only. By default its true.
/// Only works if keySync is globally enable. Else will be ignored.
......
......@@ -14,7 +14,7 @@ import pEpIOSToolboxForExtensions
import pEpIOSToolbox
#endif
protocol SmtpConnectionDelegate: class {
protocol SmtpConnectionDelegate: AnyObject {
func messageSent(_ smtpConnection: SmtpConnectionProtocol, theNotification: Notification?)
func messageNotSent(_ smtpConnection: SmtpConnectionProtocol, theNotification: Notification?)
func transactionInitiationCompleted(_ smtpConnection: SmtpConnectionProtocol, theNotification: Notification?)
......
......@@ -16,7 +16,7 @@ import pEpIOSToolboxForExtensions
import pEpIOSToolbox
#endif
protocol VerifiableAccountIMAPDelegate: class {
protocol VerifiableAccountIMAPDelegate: AnyObject {
func verified(verifier: VerifiableAccountIMAP,
result: Result<Void, Error>)
}
......
......@@ -16,7 +16,7 @@ import pEpIOSToolboxForExtensions
import pEpIOSToolbox
#endif
protocol VerifiableAccountSMTPDelegate: class {
protocol VerifiableAccountSMTPDelegate: AnyObject {
func verified(verifier: VerifiableAccountSMTP,
result: Result<Void, Error>)
}
......@@ -80,6 +80,7 @@ extension VerifiableAccountSMTP: SmtpConnectionDelegate {
}
func authenticationCompleted(_ smtpConnection: SmtpConnectionProtocol, theNotification: Notification?) {
delegate?.verified(verifier: self, result: .success(()))
}
func authenticationFailed(_ smtpConnection: SmtpConnectionProtocol, theNotification: Notification?) {
......@@ -118,9 +119,7 @@ extension VerifiableAccountSMTP: SmtpConnectionDelegate {
notifyUnexpectedCallback(name: #function)
}
func serviceInitialized(_ smtpConnection: SmtpConnectionProtocol, theNotification: Notification?) {
delegate?.verified(verifier: self, result: .success(()))
}
func serviceInitialized(_ smtpConnection: SmtpConnectionProtocol, theNotification: Notification?) { }
func serviceReconnected(_ smtpConnection: SmtpConnectionProtocol, theNotification: Notification?) {
notifyUnexpectedCallback(name: #function)
......
......@@ -57,7 +57,8 @@ class VerifiableAccountTest: PersistentStoreDrivenTestBase {
let result = checkBasicVerification() { v in
var verifiable = v
verifiable.password = "xxxxxxxxxx"
verifiable.imapPassword = "xxxxxxxxxx"
verifiable.smtpPassword = "xxxxxxxxxx"
return verifiable
}
......@@ -169,7 +170,8 @@ extension VerifiableAccountTest {
// Note: auth method is never taken from LAS. We either have OAuth2,
// as determined previously, or we will defer to pantomime to find out the best method.
verifiableAccount.authMethod = nil
verifiableAccount.password = acc.imapServer?.credentials.password
verifiableAccount.imapPassword = acc.imapServer?.credentials.password
verifiableAccount.smtpPassword = acc.smtpServer?.credentials.password
verifiableAccount.accessToken = nil
verifiableAccount.serverIMAP = acc.imapServer?.address
verifiableAccount.portIMAP = (acc.imapServer?.port)!
......
......@@ -36,7 +36,8 @@ class TestDataBase {
var imapServerType: Server.ServerType = .imap
var imapServerTransport: Server.Transport = .startTls
var imapServerPort: UInt16 = 993
var password: String?
var imapPassword: String?
var smtpPassword: String?
init(accountName: String,
idAddress: String,
......@@ -51,7 +52,8 @@ class TestDataBase {
smtpServerType: Server.ServerType,
smtpServerTransport: Server.Transport,
smtpServerPort: UInt16,
password: String) {
imapPassword: String,
smtpPassword: String) {
self.accountName = accountName
self.idAddress = idAddress
self.idUserName = idUserName
......@@ -65,7 +67,8 @@ class TestDataBase {
self.imapServerType = imapServerType
self.imapServerTransport = imapServerTransport
self.imapServerPort = imapServerPort
self.password = password
self.imapPassword = imapPassword
self.smtpPassword = smtpPassword
}
func cdAccount(context: NSManagedObjectContext = Stack.shared.mainContext) -> CdAccount {
......@@ -86,7 +89,7 @@ class TestDataBase {
smtp.transport = smtpServerTransport
let keySmtp = UUID().uuidString
CdServerCredentials.add(password: password, forKey: keySmtp)
CdServerCredentials.add(password: smtpPassword, forKey: keySmtp)
let credSmtp = CdServerCredentials(context: context)
credSmtp.loginName = smtpLoginName ?? id.address
credSmtp.key = keySmtp
......@@ -99,7 +102,7 @@ class TestDataBase {
imap.transport = imapServerTransport
let keyImap = UUID().uuidString
CdServerCredentials.add(password: password, forKey: keyImap)
CdServerCredentials.add(password: imapPassword, forKey: keyImap)
let credImap = CdServerCredentials(context: context)
credImap.loginName = imapLoginName ?? id.address
credImap.key = keyImap
......@@ -146,7 +149,8 @@ class TestDataBase {
verifiableAccount.loginNameIMAP = imapLoginName ?? idAddress
verifiableAccount.loginNameSMTP = smtpLoginName ?? idAddress
verifiableAccount.accessToken = nil
verifiableAccount.password = password
verifiableAccount.imapPassword = "xxxxxxxxxx"
verifiableAccount.smtpPassword = "xxxxxxxxxx"
verifiableAccount.serverIMAP = imapServerAddress
verifiableAccount.portIMAP = imapServerPort
......@@ -208,7 +212,8 @@ class TestDataBase {
smtpServerTransport: Server.Transport.tls,
smtpServerPort: 465,
password: "whatever_you_want"))
imapPassword: "whatever_you_want",
smtpPassword: "whatever_you_want"))
fatalError("Abstract method. Must be overridden")
}
......@@ -232,7 +237,8 @@ class TestDataBase {
smtpServerTransport: Server.Transport.plain,
smtpServerPort: 3025,
password: "pwd"))
imapPassword: "pwd",
smtpPassword: "pwd"))
}
/**
......
......@@ -11,7 +11,7 @@ import UIKit
import pEpIOSToolbox
import MessageModel
protocol LoginViewControllerDelegate: class {
protocol LoginViewControllerDelegate: AnyObject {
func loginViewControllerDidCreateNewAccount(_ loginViewController: LoginViewController)
}
......@@ -251,7 +251,8 @@ extension LoginViewController: UITextFieldDelegate {
case emailAddress:
vm.verifiableAccount.address = textField.text
case password:
vm.verifiableAccount.password = textField.text
vm.verifiableAccount.imapPassword = textField.text
vm.verifiableAccount.smtpPassword = textField.text
case user:
vm.verifiableAccount.userName = textField.text
default:
......
......@@ -197,7 +197,10 @@ final class LoginViewModel {
theVerifiableAccount.loginNameIMAP = login
theVerifiableAccount.loginNameSMTP = login
theVerifiableAccount.password = password
// Use the same password for imap and smtp when the user attemps to login in the first screen of the flow.
// If login fails, the user will be prompt to enter those passwords separately.
theVerifiableAccount.imapPassword = password
theVerifiableAccount.smtpPassword = password
theVerifiableAccount.accessToken = accessToken
theVerifiableAccount.verifiableAccountDelegate = self
......
......@@ -47,7 +47,6 @@ final class IMAPSettingsViewController: UIViewController, TextfieldResponder {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateView(animated: false)
}
......@@ -60,7 +59,7 @@ final class IMAPSettingsViewController: UIViewController, TextfieldResponder {
firstResponder(verifiableAccount.loginNameIMAP == nil)
}
@IBAction func didTapOnView(_ sender: Any) {
@IBAction private func didTapOnView(_ sender: Any) {
view.endEditing(true)
}
}
......@@ -83,7 +82,7 @@ extension IMAPSettingsViewController: UITextFieldDelegate {
Log.shared.errorAndCrash("Fail to get manualAccountSetupView")
return true
}
if textField == setupView.fourthTextField {
if textField == setupView.fifthTextField {
view.endEditing(true)
presentActionSheetWithTransportSecurityValues(textField)
return false
......@@ -98,9 +97,9 @@ extension IMAPSettingsViewController: UITextFieldDelegate {
Log.shared.errorAndCrash("Fail to get manualAccountSetupView")
return true
}
if textField == setupView.thirdTextField {
if textField == setupView.fourthTextField {
guard var text = textField.text,
let range = Range(range, in: text) else {
let range = Range(range, in: text) else {
Log.shared.errorAndCrash("Fail to get textField text or range")
return true
}
......@@ -147,6 +146,7 @@ extension IMAPSettingsViewController: ManualAccountSetupViewDelegate {
performSegue(withIdentifier: .SMTPSettings, sender: self)
}
// Username
func didChangeFirst(_ textField: UITextField) {
guard var verifiableAccount = verifiableAccount else {
Log.shared.errorAndCrash("No Verifiable account")
......@@ -156,19 +156,30 @@ extension IMAPSettingsViewController: ManualAccountSetupViewDelegate {
updateView()
}
// Password
func didChangeSecond(_ textField: UITextField) {
guard var verifiableAccount = verifiableAccount else {
Log.shared.errorAndCrash("No Verifiable account")
return
}
verifiableAccount.serverIMAP = textField.text
verifiableAccount.imapPassword = textField.text
}
// Server
func didChangeThird(_ textField: UITextField) {
guard var verifiableAccount = verifiableAccount else {
Log.shared.errorAndCrash("No Verifiable account")
return
}
verifiableAccount.serverIMAP = textField.text
}
// Port
func didChangeFourth(_ textField: UITextField) {
guard let text = textField.text,
let port = UInt16(text) else {
//If not UInt16 then do nothing. Example empty string
return
let port = UInt16(text) else {
//If not UInt16 then do nothing. Example empty string
return
}
guard var verifiableAccount = verifiableAccount else {
Log.shared.errorAndCrash("No Verifiable account")
......@@ -177,20 +188,22 @@ extension IMAPSettingsViewController: ManualAccountSetupViewDelegate {
verifiableAccount.portIMAP = port
}
func didChangeFourth(_ textField: UITextField) {
//Do nothing, changes saved in model and textField in the bock of alert
// Transport security
func didChangeFifth(_ textField: UITextField) {
//Do nothing, changes are saved in model and textField in the block of alert.
}
}
// MARK: - Private
extension IMAPSettingsViewController {
private func setUpTextFieldsInputTraits() {
guard let setupView = manualAccountSetupContainerView.setupView else {
Log.shared.errorAndCrash("Fail to get manualAccountSetupView")
return
}
setupView.thirdTextField.keyboardType = .numberPad
setupView.fourthTextField.keyboardType = .numberPad
}
private func setUpViewLocalizableTexts() {
......@@ -212,23 +225,31 @@ extension IMAPSettingsViewController {
let userNamePlaceholder = NSLocalizedString("User Name", comment: "User Name placeholder for manual account IMAP setup")
setupView.firstTextField.placeholder = userNamePlaceholder
let passwordPlaceholder = NSLocalizedString("Password", comment: "Password placeholder for manual account IMAP setup")
setupView.secondTextField.placeholder = passwordPlaceholder
setupView.secondTextField.isSecureTextEntry = true
let serverPlaceholder = NSLocalizedString("Server", comment: "Server placeholder for manual account IMAP setup")
setupView.secondTextField.placeholder = serverPlaceholder
setupView.thirdTextField.placeholder = serverPlaceholder
let portPlaceholder = NSLocalizedString("Port", comment: "Port placeholder for manual account IMAP setup")
setupView.thirdTextField.placeholder = portPlaceholder
setupView.fourthTextField.placeholder = portPlaceholder
let TransportSecurityPlaceholder = NSLocalizedString("Transport Security", comment: "TransportSecurity placeholder for manual account IMAP setup")
setupView.fourthTextField.placeholder = TransportSecurityPlaceholder
let transportSecurityPlaceholder = NSLocalizedString("Transport Security", comment: "TransportSecurity placeholder for manual account IMAP setup")
setupView.fifthTextField.placeholder = transportSecurityPlaceholder
}
private func presentActionSheetWithTransportSecurityValues(_ sender: UITextField) {
let title = NSLocalizedString("Transport protocol", comment: "UI alert title for transport protocol")
let message = NSLocalizedString("Choose a Security protocol for your accont", comment: "UI alert message for transport protocol")
let alertController = UIUtils.actionSheet(title: title, message: message)
let block: (ConnectionTransport) -> () = { transport in
let block: (ConnectionTransport) -> () = { [weak self] transport in
sender.text = transport.localizedString()
self.verifiableAccount?.transportIMAP = transport
guard let me = self else {
Log.shared.errorAndCrash("Lost myself")
return
}
me.verifiableAccount?.transportIMAP = transport
}
if let popoverPresentationController = alertController.popoverPresentationController {
......@@ -260,15 +281,15 @@ extension IMAPSettingsViewController {
}
setupView.firstTextField.set(text: verifiableAccount.loginNameIMAP,
animated: animated)
setupView.secondTextField.set(text: verifiableAccount.serverIMAP,
setupView.secondTextField.set(text: verifiableAccount.imapPassword,
animated: animated)
setupView.thirdTextField.set(text: String(verifiableAccount.portIMAP),
setupView.thirdTextField.set(text: verifiableAccount.serverIMAP,
animated: animated)