IOS-1480 Move NetworkService and its worker. refactor
authorDirk Zimmermann <dz@pep.security>
Tue, 12 Feb 2019 16:04:14 +0100
branchrefactor
changeset 761903beef1152ba
parent 7615 4b7c33884831
child 7620 7caf4d149c17
IOS-1480 Move NetworkService and its worker.
pEpForiOS.xcodeproj/project.pbxproj
pEpForiOS/Network/Service/NetworkService.swift
pEpForiOS/Network/Service/NetworkServiceWorker.swift
     1.1 --- a/pEpForiOS.xcodeproj/project.pbxproj	Tue Feb 12 13:55:08 2019 +0100
     1.2 +++ b/pEpForiOS.xcodeproj/project.pbxproj	Tue Feb 12 16:04:14 2019 +0100
     1.3 @@ -257,7 +257,6 @@
     1.4  		43122B191DF5B48B00610253 /* EmailService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43122B141DF5B48B00610253 /* EmailService.swift */; };
     1.5  		43122B1A1DF5B48B00610253 /* ImapService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43122B151DF5B48B00610253 /* ImapService.swift */; };
     1.6  		43122B1B1DF5B48B00610253 /* SmtpService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43122B161DF5B48B00610253 /* SmtpService.swift */; };
     1.7 -		43122B3C1DF5B75000610253 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43122B3B1DF5B75000610253 /* NetworkService.swift */; };
     1.8  		43122B3E1DF5BB6600610253 /* MySelfOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43122B3D1DF5BB6600610253 /* MySelfOperation.swift */; };
     1.9  		431394A91E4B03AA00D92F33 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 431394A81E4B03AA00D92F33 /* Settings.bundle */; };
    1.10  		4315E4BF2011FD6900F68763 /* AuthMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4315E4BE2011FD6900F68763 /* AuthMethod.swift */; };
    1.11 @@ -391,7 +390,6 @@
    1.12  		438052871FE3E1B100ACF729 /* OAuth2AuthorizationFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 438052861FE3E1B100ACF729 /* OAuth2AuthorizationFactoryProtocol.swift */; };
    1.13  		438052891FE3E24400ACF729 /* OAuth2AuthorizationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 438052881FE3E24400ACF729 /* OAuth2AuthorizationProtocol.swift */; };
    1.14  		4380528B1FE3E2E800ACF729 /* OAuth2AuthorizationURLHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4380528A1FE3E2E800ACF729 /* OAuth2AuthorizationURLHandlerProtocol.swift */; };
    1.15 -		4381D0FF1E51A124002743C3 /* NetworkServiceWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4381D0FE1E51A124002743C3 /* NetworkServiceWorker.swift */; };
    1.16  		43826F87212FF91D00BF1F7B /* Substring+Email.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43826F86212FF91D00BF1F7B /* Substring+Email.swift */; };
    1.17  		438281831E891B7E00087343 /* DateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 438281821E891B7E00087343 /* DateTests.swift */; };
    1.18  		4382E6431CC600FF00AA27EA /* PersistentImapFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4382E6421CC600FF00AA27EA /* PersistentImapFolder.swift */; };
    1.19 @@ -866,7 +864,6 @@
    1.20  		43122B141DF5B48B00610253 /* EmailService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailService.swift; sourceTree = "<group>"; };
    1.21  		43122B151DF5B48B00610253 /* ImapService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImapService.swift; sourceTree = "<group>"; };
    1.22  		43122B161DF5B48B00610253 /* SmtpService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SmtpService.swift; sourceTree = "<group>"; };
    1.23 -		43122B3B1DF5B75000610253 /* NetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = "<group>"; };
    1.24  		43122B3D1DF5BB6600610253 /* MySelfOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MySelfOperation.swift; sourceTree = "<group>"; };
    1.25  		431394A81E4B03AA00D92F33 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
    1.26  		4315E4BE2011FD6900F68763 /* AuthMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthMethod.swift; sourceTree = "<group>"; };
    1.27 @@ -1024,7 +1021,6 @@
    1.28  		438052861FE3E1B100ACF729 /* OAuth2AuthorizationFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuth2AuthorizationFactoryProtocol.swift; sourceTree = "<group>"; };
    1.29  		438052881FE3E24400ACF729 /* OAuth2AuthorizationProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuth2AuthorizationProtocol.swift; sourceTree = "<group>"; };
    1.30  		4380528A1FE3E2E800ACF729 /* OAuth2AuthorizationURLHandlerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuth2AuthorizationURLHandlerProtocol.swift; sourceTree = "<group>"; };
    1.31 -		4381D0FE1E51A124002743C3 /* NetworkServiceWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkServiceWorker.swift; sourceTree = "<group>"; };
    1.32  		43826F86212FF91D00BF1F7B /* Substring+Email.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Substring+Email.swift"; sourceTree = "<group>"; };
    1.33  		438281821E891B7E00087343 /* DateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateTests.swift; sourceTree = "<group>"; };
    1.34  		4382E6421CC600FF00AA27EA /* PersistentImapFolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PersistentImapFolder.swift; sourceTree = "<group>"; };
    1.35 @@ -1990,8 +1986,6 @@
    1.36  				431E58F41ED57F4D00EFA77F /* AccountVerificationService */,
    1.37  				1587D1622049BE7D002C2157 /* ImapConnectionDataCache.swift */,
    1.38  				432142651E8FD6A400FBE987 /* ServiceUtil.swift */,
    1.39 -				43122B3B1DF5B75000610253 /* NetworkService.swift */,
    1.40 -				4381D0FE1E51A124002743C3 /* NetworkServiceWorker.swift */,
    1.41  				432142631E8FD66900FBE987 /* FetchNumberOfNewMailsService.swift */,
    1.42  				432198E71DF6B51B00318A74 /* LoginImapOperation.swift */,
    1.43  				4348EF0C1E27ABA100F441A9 /* LoginSmtpOperation.swift */,
    1.44 @@ -3340,7 +3334,6 @@
    1.45  				4341980520AB037E0062F7F6 /* CGImageSource+Extension.swift in Sources */,
    1.46  				43D755FC1F26382B006F933A /* EmailConnectInfo+Extension.swift in Sources */,
    1.47  				15874BCB2127493E00A3A4A6 /* AccountVerificationResultDelegate.swift in Sources */,
    1.48 -				4381D0FF1E51A124002743C3 /* NetworkServiceWorker.swift in Sources */,
    1.49  				43E657E71F3CAB310014CBEC /* HtmlToMarkdownSaxParser.swift in Sources */,
    1.50  				43E1619120D7B2D6003F1514 /* UpdateThreadListDelegate.swift in Sources */,
    1.51  				155475642137FD96005A52D0 /* FolderType+Extensions.swift in Sources */,
    1.52 @@ -3358,7 +3351,6 @@
    1.53  				43AA825B1E9BC5FF00ABD5A8 /* AttachmentViewContainer.swift in Sources */,
    1.54  				490CEBA921020BF900E8579C /* LoginViewController+Keyboard.swift in Sources */,
    1.55  				4326D3FF1EFBC8DB0016AB0D /* FolderInfoOperation.swift in Sources */,
    1.56 -				43122B3C1DF5B75000610253 /* NetworkService.swift in Sources */,
    1.57  				1594C1D021281FCD00CB06E2 /* Server+Fetching.swift in Sources */,
    1.58  				152A39D721905C3E00D9F8E4 /* TextAttachment.swift in Sources */,
    1.59  				43ED53711CC77F95006AB156 /* IMAPSettingsTableViewController.swift in Sources */,
     2.1 --- a/pEpForiOS/Network/Service/NetworkService.swift	Tue Feb 12 13:55:08 2019 +0100
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,220 +0,0 @@
     2.4 -//
     2.5 -//  NetworkService.swift
     2.6 -//  pEpForiOS
     2.7 -//
     2.8 -//  Created by hernani on 10/10/16.
     2.9 -//  Copyright © 2016 p≡p Security S.A. All rights reserved.
    2.10 -//
    2.11 -
    2.12 -import Foundation
    2.13 -
    2.14 -import MessageModel
    2.15 -
    2.16 -public protocol NetworkServiceDelegate: class {
    2.17 -    /// Called finishing the last sync loop.
    2.18 -    /// No further sync loop will be triggered after this call.
    2.19 -    /// All operations finished before this call.
    2.20 -    func networkServiceDidFinishLastSyncLoop(service:NetworkService)
    2.21 -
    2.22 -    /// Called after graceful shutdown.
    2.23 -    func networkServiceDidCancel(service:NetworkService)
    2.24 -}
    2.25 -
    2.26 -/**
    2.27 - * Provides all the IMAP and SMTP syncing. Will constantly run in the background.
    2.28 - */
    2.29 -public class NetworkService {
    2.30 -    public class ServiceConfig {
    2.31 -        /**
    2.32 -         Folders (other than inbox) that the user looked at
    2.33 -         in the last `timeIntervalForInterestingFolders`
    2.34 -         are considered sync-worthy.
    2.35 -         */
    2.36 -        public var timeIntervalForInterestingFolders: TimeInterval = 60 * 60 * 24
    2.37 -        
    2.38 -        /**
    2.39 -         Amount of time to "sleep" between complete syncs of all accounts.
    2.40 -         */
    2.41 -        public var sleepTimeInSeconds: Double
    2.42 -
    2.43 -        public var sendLayerDelegate: SendLayerDelegate?
    2.44 -
    2.45 -        let parentName: String
    2.46 -        let mySelfer: KickOffMySelfProtocol?
    2.47 -        let backgrounder: BackgroundTaskProtocol?
    2.48 -        var errorPropagator: ErrorPropagator?
    2.49 -
    2.50 -        init(sleepTimeInSeconds: Double,
    2.51 -             parentName: String,
    2.52 -             mySelfer: KickOffMySelfProtocol?,
    2.53 -             backgrounder: BackgroundTaskProtocol?,
    2.54 -             errorPropagator: ErrorPropagator?) {
    2.55 -            self.sleepTimeInSeconds = sleepTimeInSeconds
    2.56 -            self.parentName = parentName
    2.57 -            self.mySelfer = mySelfer
    2.58 -            self.backgrounder = backgrounder
    2.59 -            self.errorPropagator = errorPropagator
    2.60 -        }
    2.61 -    }
    2.62 -
    2.63 -    var serviceConfig: ServiceConfig
    2.64 -    public private(set) var currentWorker: NetworkServiceWorker?
    2.65 -    var newMailsService: FetchNumberOfNewMailsService?
    2.66 -    public weak var delegate: NetworkServiceDelegate?
    2.67 -    private var imapConnectionDataCache: ImapConnectionDataCache?
    2.68 -    // UNIT TEST ONLY
    2.69 -    public weak var unitTestDelegate: NetworkServiceUnitTestDelegate?
    2.70 -
    2.71 -    /**
    2.72 -     Amount of time to "sleep" between complete syncs of all accounts.
    2.73 -     */
    2.74 -    public var sleepTimeInSeconds: Double {
    2.75 -        get {
    2.76 -            return serviceConfig.sleepTimeInSeconds
    2.77 -        }
    2.78 -        set {
    2.79 -            serviceConfig.sleepTimeInSeconds = newValue
    2.80 -        }
    2.81 -    }
    2.82 -
    2.83 -    public var timeIntervalForInterestingFolders: TimeInterval {
    2.84 -        get {
    2.85 -            return serviceConfig.timeIntervalForInterestingFolders
    2.86 -        }
    2.87 -        set {
    2.88 -            serviceConfig.timeIntervalForInterestingFolders = newValue
    2.89 -        }
    2.90 -    }
    2.91 -
    2.92 -    public init(sleepTimeInSeconds: Double = 5.0,
    2.93 -                parentName: String = #function,
    2.94 -                backgrounder: BackgroundTaskProtocol? = nil,
    2.95 -                mySelfer: KickOffMySelfProtocol? = nil,
    2.96 -                errorPropagator: ErrorPropagator? = nil) {
    2.97 -        serviceConfig = ServiceConfig(sleepTimeInSeconds: sleepTimeInSeconds,
    2.98 -                                      parentName: parentName,
    2.99 -                                      mySelfer: mySelfer ??
   2.100 -                                        DefaultMySelfer( parentName: parentName,
   2.101 -                                                         backgrounder: backgrounder),
   2.102 -                                      backgrounder: backgrounder,
   2.103 -                                      errorPropagator: errorPropagator)
   2.104 -        currentWorker = NetworkServiceWorker(serviceConfig: serviceConfig,
   2.105 -                             imapConnectionDataCache: nil)
   2.106 -        currentWorker?.delegate = self
   2.107 -        currentWorker?.unitTestDelegate = self
   2.108 -    }
   2.109 -
   2.110 -    /**
   2.111 -     Start endlessly synchronizing in the background.
   2.112 -     */
   2.113 -    public func start() {
   2.114 -        if currentWorker == nil {
   2.115 -            currentWorker = NetworkServiceWorker(serviceConfig: serviceConfig,
   2.116 -                                                 imapConnectionDataCache: imapConnectionDataCache)
   2.117 -        }
   2.118 -        currentWorker?.delegate = self
   2.119 -        currentWorker?.unitTestDelegate = self
   2.120 -        currentWorker?.start()
   2.121 -    }
   2.122 -
   2.123 -    /// Stop endlessly synchronizing in the background, syncs all pending changes triggered by the
   2.124 -    /// user with server.
   2.125 -    /// Calls NetworkServiceDelegate networkServiceDidFinishLastSyncLoop() when done.
   2.126 -    public func processAllUserActionsAndstop() {
   2.127 -        saveCurrentWorkersConfigAndImapConnectionCache()
   2.128 -        currentWorker?.stop()
   2.129 -    }
   2.130 -
   2.131 -    /**
   2.132 -     Cancel worker and services.
   2.133 -     */
   2.134 -    public func cancel() {
   2.135 -        saveCurrentWorkersConfigAndImapConnectionCache()
   2.136 -        currentWorker?.cancel()
   2.137 -        // Only to make sure. Should not be required.
   2.138 -        newMailsService?.stop()
   2.139 -    }
   2.140 -
   2.141 -    public func checkForNewMails(completionHandler: @escaping (_ numNewMails: Int?) -> ()) {
   2.142 -        newMailsService = FetchNumberOfNewMailsService(imapConnectionDataCache: nil)
   2.143 -        newMailsService?.start(completionBlock: completionHandler)
   2.144 -    }
   2.145 -
   2.146 -    // MARK: -
   2.147 -
   2.148 -    private func saveCurrentWorkersConfigAndImapConnectionCache() {
   2.149 -        if let cache = currentWorker?.imapConnectionDataCache {
   2.150 -            imapConnectionDataCache = cache
   2.151 -        }
   2.152 -        if let config = currentWorker?.serviceConfig {
   2.153 -            serviceConfig = config
   2.154 -        }
   2.155 -    }
   2.156 -}
   2.157 -
   2.158 -// MARK: - SendLayerProtocol
   2.159 -
   2.160 -extension NetworkService: SendLayerProtocol {
   2.161 -    public var sendLayerDelegate: SendLayerDelegate? {
   2.162 -        set {
   2.163 -            serviceConfig.sendLayerDelegate = newValue
   2.164 -        }
   2.165 -        get {
   2.166 -            return serviceConfig.sendLayerDelegate
   2.167 -        }
   2.168 -    }
   2.169 -}
   2.170 -
   2.171 -// MARK: - NetworkServiceWorkerDelegate
   2.172 -
   2.173 -extension NetworkService: NetworkServiceWorkerDelegate {
   2.174 -    public func networkServiceWorkerDidFinishLastSyncLoop(worker: NetworkServiceWorker) {
   2.175 -        GCD.onMain { [weak self] in
   2.176 -            guard let me = self else {
   2.177 -                Logger.frontendLogger.lostMySelf()
   2.178 -                return
   2.179 -            }
   2.180 -            me.delegate?.networkServiceDidFinishLastSyncLoop(service: me)
   2.181 -        }
   2.182 -    }
   2.183 -
   2.184 -    public func networkServiceWorkerDidCancel(worker: NetworkServiceWorker) {
   2.185 -        GCD.onMain { [weak self] in
   2.186 -            guard let me = self else {
   2.187 -                Logger.frontendLogger.lostMySelf()
   2.188 -                return
   2.189 -            }
   2.190 -            me.delegate?.networkServiceDidCancel(service: me)
   2.191 -        }
   2.192 -    }
   2.193 -
   2.194 -    public func networkServiceWorker(_ worker: NetworkServiceWorker, errorOccured error: Error) {
   2.195 -        GCD.onMain { [weak self] in
   2.196 -            guard let me = self else {
   2.197 -                Logger.frontendLogger.lostMySelf()
   2.198 -                return
   2.199 -            }
   2.200 -            me.serviceConfig.errorPropagator?.report(error: error)
   2.201 -        }
   2.202 -    }
   2.203 -}
   2.204 -
   2.205 - // MARK: - UNIT TEST ONLY
   2.206 -
   2.207 -public protocol NetworkServiceUnitTestDelegate: class {
   2.208 -    /** Called after each account sync */
   2.209 -    func networkServiceDidSync(service: NetworkService, accountInfo: AccountConnectInfo,
   2.210 -                               errorProtocol: ServiceErrorProtocol)
   2.211 -}
   2.212 -
   2.213 -// MARK: NetworkServiceWorkerUnitTestDelegate
   2.214 -
   2.215 -extension NetworkService: NetworkServiceWorkerUnitTestDelegate {
   2.216 -    public func testWorkerDidSync(worker: NetworkServiceWorker,
   2.217 -                                            accountInfo: AccountConnectInfo,
   2.218 -                                            errorProtocol: ServiceErrorProtocol) {
   2.219 -        self.unitTestDelegate?.networkServiceDidSync(service: self,
   2.220 -                                                     accountInfo: accountInfo,
   2.221 -                                                     errorProtocol: errorProtocol)
   2.222 -    }
   2.223 -}
     3.1 --- a/pEpForiOS/Network/Service/NetworkServiceWorker.swift	Tue Feb 12 13:55:08 2019 +0100
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,739 +0,0 @@
     3.4 -//
     3.5 -//  NetworkServiceWorker.swift
     3.6 -//  pEpForiOS
     3.7 -//
     3.8 -//  Created by Dirk Zimmermann on 13/02/2017.
     3.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
    3.10 -//
    3.11 -
    3.12 -import UIKit
    3.13 -import CoreData
    3.14 -
    3.15 -import MessageModel
    3.16 -
    3.17 -public protocol NetworkServiceWorkerDelegate: class {
    3.18 -    /// Called finishing the last sync loop.
    3.19 -    /// No further sync loop will be triggered after this call.
    3.20 -    /// All operations finished before this call.
    3.21 -    /// - Parameters:
    3.22 -    ///   - worker: sender
    3.23 -    func networkServiceWorkerDidFinishLastSyncLoop(worker: NetworkServiceWorker)
    3.24 -
    3.25 -    /// Called after clean shutdown
    3.26 -    /// - Parameters:
    3.27 -    ///   - worker: sender
    3.28 -    func networkServiceWorkerDidCancel(worker: NetworkServiceWorker)
    3.29 -
    3.30 -    /// Used to report errors in operation line.
    3.31 -    /// - Parameters:
    3.32 -    ///   - worker: sender
    3.33 -    ///   - error: error reported by an operation in operation line
    3.34 -    func networkServiceWorker(_ worker: NetworkServiceWorker, errorOccured error: Error)
    3.35 -}
    3.36 -
    3.37 -open class NetworkServiceWorker {
    3.38 -    public struct FolderInfo {
    3.39 -        public let name: String
    3.40 -        public let folderType: FolderType
    3.41 -        public let firstUID: UInt?
    3.42 -        public let lastUID: UInt?
    3.43 -        public let folderID: NSManagedObjectID?
    3.44 -    }
    3.45 -
    3.46 -    class ObjectObserver: NSObject {
    3.47 -        let backgroundQueue: OperationQueue
    3.48 -        let operationCountKeyPath: String
    3.49 -        let myComp: String
    3.50 -
    3.51 -        init(backgroundQueue: OperationQueue, operationCountKeyPath: String, myComp: String) {
    3.52 -            self.backgroundQueue = backgroundQueue
    3.53 -            self.operationCountKeyPath = operationCountKeyPath
    3.54 -            self.myComp = myComp
    3.55 -        }
    3.56 -
    3.57 -        override open func observeValue(forKeyPath keyPath: String?, of object: Any?,
    3.58 -                                        change: [NSKeyValueChangeKey : Any]?,
    3.59 -                                        context: UnsafeMutableRawPointer?) {
    3.60 -            guard let newValue = change?[NSKeyValueChangeKey.newKey] else {
    3.61 -                super.observeValue(forKeyPath: keyPath, of: object, change: change,
    3.62 -                                   context: context)
    3.63 -                return
    3.64 -            }
    3.65 -            if keyPath == operationCountKeyPath {
    3.66 -                let _ = (newValue as? NSNumber)?.intValue
    3.67 -            } else {
    3.68 -                super.observeValue(forKeyPath: keyPath, of: object, change: change,
    3.69 -                                   context: context)
    3.70 -            }
    3.71 -        }
    3.72 -    }
    3.73 -
    3.74 -    public weak var delegate: NetworkServiceWorkerDelegate?
    3.75 -    // UNIT TEST ONLY
    3.76 -    weak var unitTestDelegate: NetworkServiceWorkerUnitTestDelegate?
    3.77 -
    3.78 -    var serviceConfig: NetworkService.ServiceConfig
    3.79 -
    3.80 -    var cancelled = false
    3.81 -
    3.82 -    let workerQueue = DispatchQueue(
    3.83 -        label: "NetworkService", qos: .utility, target: nil)
    3.84 -    let backgroundQueue: OperationQueue = {
    3.85 -        let queue = OperationQueue()
    3.86 -        return queue
    3.87 -    }()
    3.88 -
    3.89 -    let operationCountKeyPath = "operationCount"
    3.90 -
    3.91 -    let context = Record.Context.background
    3.92 -
    3.93 -    private(set) var imapConnectionDataCache = ImapConnectionDataCache()
    3.94 -
    3.95 -    init(serviceConfig: NetworkService.ServiceConfig,
    3.96 -         imapConnectionDataCache: ImapConnectionDataCache? = nil) {
    3.97 -        self.serviceConfig = serviceConfig
    3.98 -        if let cache = imapConnectionDataCache {
    3.99 -            self.imapConnectionDataCache = cache
   3.100 -        }
   3.101 -    }
   3.102 -
   3.103 -    /**
   3.104 -     Start endlessly synchronizing in the background.
   3.105 -     */
   3.106 -    public func start() {
   3.107 -        cancelled = false
   3.108 -        imapConnectionDataCache.reset()
   3.109 -        self.process()
   3.110 -    }
   3.111 -
   3.112 -    /**
   3.113 -     Cancel all background operations, finish main loop.
   3.114 -     */
   3.115 -    public func cancel() {
   3.116 -        let myComp = #function
   3.117 -
   3.118 -        self.cancelled = true
   3.119 -        self.backgroundQueue.cancelAllOperations()
   3.120 -
   3.121 -        workerQueue.async {[weak self] in
   3.122 -            guard let me = self else {
   3.123 -                Logger.backendLogger.lostMySelf()
   3.124 -                return
   3.125 -            }
   3.126 -            let observer = ObjectObserver(
   3.127 -                backgroundQueue: me.backgroundQueue,
   3.128 -                operationCountKeyPath: me.operationCountKeyPath, myComp: myComp)
   3.129 -            me.backgroundQueue.addObserver(observer,
   3.130 -                                           forKeyPath: me.operationCountKeyPath,
   3.131 -                                             options: [.initial, .new],
   3.132 -                                             context: nil)
   3.133 -            me.backgroundQueue.waitUntilAllOperationsAreFinished()
   3.134 -            me.backgroundQueue.removeObserver(observer, forKeyPath: me.operationCountKeyPath)
   3.135 -            me.delegate?.networkServiceWorkerDidCancel(worker: me)
   3.136 -        }
   3.137 -    }
   3.138 -
   3.139 -    /// Makes sure all local changes are synced to the server and then stops.
   3.140 -    /// Calls delegate networkServiceWorkerDidFinishLastSyncLoop when done.
   3.141 -    public func stop() {
   3.142 -        syncLocalChangesWithServerAndStop()
   3.143 -    }
   3.144 -
   3.145 -    /// Stops all queued operations, syncs all local changes with the server and informs the
   3.146 -    /// delegate when done.
   3.147 -    public func syncLocalChangesWithServerAndStop() {
   3.148 -        cancelled = true
   3.149 -        workerQueue.async { [weak self] in
   3.150 -            guard let me = self else {
   3.151 -                Logger.backendLogger.lostMySelf()
   3.152 -                return
   3.153 -            }
   3.154 -            // Cancel the current sync loop ...
   3.155 -            me.backgroundQueue.cancelAllOperations()
   3.156 -            me.backgroundQueue.waitUntilAllOperationsAreFinished()
   3.157 -            // ... and run a minimized sync loop that assures all local changes are synced to
   3.158 -            //     the server.
   3.159 -            let connectInfos = ServiceUtil.gatherConnectInfos(context: me.context,
   3.160 -                                                              accounts: me.fetchAccounts())
   3.161 -            let operationLines =
   3.162 -                me.buildSyncLocalChangesOperationLines(accountConnectInfos: connectInfos)
   3.163 -            for operartionLine in operationLines {
   3.164 -                me.backgroundQueue.addOperations(operartionLine.operations,
   3.165 -                                                 waitUntilFinished: false)
   3.166 -            }
   3.167 -            me.backgroundQueue.waitUntilAllOperationsAreFinished()
   3.168 -            // Inform delegate that we are done.
   3.169 -            me.delegate?.networkServiceWorkerDidFinishLastSyncLoop(worker: me)
   3.170 -        }
   3.171 -    }
   3.172 -
   3.173 -    /**
   3.174 -     Start endlessly synchronizing in the background.
   3.175 -     */
   3.176 -    public func process(repeatProcess: Bool = true) {
   3.177 -        workerQueue.async {
   3.178 -            self.processAllInternal(repeatProcess: repeatProcess)
   3.179 -        }
   3.180 -    }
   3.181 -
   3.182 -    /**
   3.183 -     Main entry point for the main loop.
   3.184 -     Implements RFC 4549 (https://tools.ietf.org/html/rfc4549).
   3.185 -     */
   3.186 -    func processAllInternal(repeatProcess: Bool = true) {
   3.187 -        if !cancelled {
   3.188 -            let connectInfos = ServiceUtil.gatherConnectInfos(context: context,
   3.189 -                                                              accounts: fetchAccounts())
   3.190 -            let operationLines = buildOperationLines(
   3.191 -                accountConnectInfos: connectInfos)
   3.192 -            processOperationLinesInternal(operationLines: operationLines,
   3.193 -                                          repeatProcess: repeatProcess)
   3.194 -        }
   3.195 -    }
   3.196 -
   3.197 -    func fetchAccounts() -> [CdAccount] {
   3.198 -        var result = [CdAccount]()
   3.199 -        context.performAndWait {
   3.200 -            result =  CdAccount.all() ?? []
   3.201 -        }
   3.202 -        return result
   3.203 -    }
   3.204 -
   3.205 -    func buildSmtpOperations(accountInfo: AccountConnectInfo,
   3.206 -                             errorContainer: ServiceErrorProtocol,
   3.207 -                             opSmtpFinished: Operation,
   3.208 -                             lastOperation: Operation?) -> (BaseOperation?, [Operation]) {
   3.209 -        // Do not bother with SMTP server if we have nothing to send
   3.210 -        if !EncryptAndSendOperation.outgoingMailsExist(in: Record.Context.background,
   3.211 -                                                       forAccountWith: accountInfo.accountID) {
   3.212 -            return (nil, [])
   3.213 -        }
   3.214 -
   3.215 -        guard let smtpCI = accountInfo.smtpConnectInfo else {
   3.216 -            return (nil, [])
   3.217 -        }
   3.218 -        // 3.a Items not associated with any mailbox (e.g., SMTP send)
   3.219 -        let smtpSendData = SmtpSendData(connectInfo: smtpCI)
   3.220 -        let loginOp = LoginSmtpOperation(parentName: serviceConfig.parentName,
   3.221 -                                         smtpSendData: smtpSendData,
   3.222 -                                         errorContainer: errorContainer)
   3.223 -        loginOp.completionBlock = {
   3.224 -            loginOp.completionBlock = nil
   3.225 -        }
   3.226 -        if let lastOp = lastOperation {
   3.227 -            loginOp.addDependency(lastOp)
   3.228 -        }
   3.229 -        opSmtpFinished.addDependency(loginOp)
   3.230 -        var operations = [Operation]()
   3.231 -        operations.append(loginOp)
   3.232 -
   3.233 -        let sendOp = EncryptAndSendOperation(
   3.234 -            parentName: serviceConfig.parentName, smtpSendData: smtpSendData,
   3.235 -            errorContainer: errorContainer)
   3.236 -        sendOp.addDependency(loginOp)
   3.237 -        opSmtpFinished.addDependency(sendOp)
   3.238 -        operations.append(sendOp)
   3.239 -
   3.240 -        return (sendOp, operations)
   3.241 -    }
   3.242 -
   3.243 -    func buildAppendOperation(imapSyncData: ImapSyncData,
   3.244 -                              errorContainer: ServiceErrorProtocol) -> Operation {
   3.245 -        let resultOp = SelfReferencingOperation() { operation in
   3.246 -            guard let operation = operation else {
   3.247 -                Logger.backendLogger.lostMySelf()
   3.248 -                return
   3.249 -            }
   3.250 -            if operation.isCancelled {
   3.251 -                return
   3.252 -            }
   3.253 -            let queue = OperationQueue()
   3.254 -            var operations = [Operation]()
   3.255 -            var lastOp = Operation()
   3.256 -            operations.append(lastOp)
   3.257 -
   3.258 -            MessageModel.performAndWait {
   3.259 -                if operation.isCancelled {
   3.260 -                    return
   3.261 -                }
   3.262 -                let folders = AppendMailsOperation.foldersContainingMarkedForAppend(connectInfo:
   3.263 -                    imapSyncData.connectInfo)
   3.264 -                for folder in folders {
   3.265 -                    let op = AppendMailsOperation(folder: folder, imapSyncData: imapSyncData)
   3.266 -                    op.addDependency(lastOp)
   3.267 -                    lastOp = op
   3.268 -                    operations.append(op)
   3.269 -                }
   3.270 -            }
   3.271 -            if operation.isCancelled {
   3.272 -                return
   3.273 -            }
   3.274 -            queue.addOperations(operations, waitUntilFinished: true)
   3.275 -        }
   3.276 -        return resultOp
   3.277 -    }
   3.278 -
   3.279 -    func buildUidMoveToFolderOperation(imapSyncData: ImapSyncData,
   3.280 -                                       errorContainer: ServiceErrorProtocol) -> Operation {
   3.281 -        let resultOp = SelfReferencingOperation() { operation in
   3.282 -            guard let operation = operation else {
   3.283 -                Logger.backendLogger.lostMySelf()
   3.284 -                return
   3.285 -            }
   3.286 -            if operation.isCancelled {
   3.287 -                return
   3.288 -            }
   3.289 -            let queue = OperationQueue()
   3.290 -            var operations = [Operation]()
   3.291 -            var lastOp = Operation()
   3.292 -            operations.append(lastOp)
   3.293 -            MessageModel.performAndWait {
   3.294 -                if operation.isCancelled {
   3.295 -                    return
   3.296 -                }
   3.297 -                let folders =
   3.298 -                    MoveToFolderOperation.foldersContainingMarkedForMoveToFolder(connectInfo:
   3.299 -                    imapSyncData.connectInfo)
   3.300 -                for folder in folders {
   3.301 -                    let op = MoveToFolderOperation(imapSyncData: imapSyncData,
   3.302 -                                                   errorContainer: errorContainer,
   3.303 -                                                   folder: folder)
   3.304 -                    op.addDependency(lastOp)
   3.305 -                    lastOp = op
   3.306 -                    operations.append(op)
   3.307 -                }
   3.308 -            }
   3.309 -            if operation.isCancelled {
   3.310 -                return
   3.311 -            }
   3.312 -            queue.addOperations(operations, waitUntilFinished: true)
   3.313 -        }
   3.314 -        return resultOp
   3.315 -    }
   3.316 -
   3.317 -    /**
   3.318 -     Determine "interesting" folder names that should be synced, and for each:
   3.319 -     Determine current firstUID, lastUID, and store it (for later sync of existing messages).
   3.320 -     - Note: Interesting mailboxes are Inbox (always), and the most recently looked at
   3.321 -     folders.
   3.322 -     */
   3.323 -    public func determineInterestingFolders(accountInfo: AccountConnectInfo) -> [FolderInfo] {
   3.324 -        var folderInfos = [FolderInfo]()
   3.325 -        let context = Record.Context.background
   3.326 -        context.performAndWait {
   3.327 -            guard let account = context.object(with: accountInfo.accountID) as? CdAccount else {
   3.328 -                return
   3.329 -            }
   3.330 -            let earlierTimestamp = Date(
   3.331 -                timeIntervalSinceNow: -self.serviceConfig.timeIntervalForInterestingFolders)
   3.332 -            let pInteresting = NSPredicate(
   3.333 -                format: "account = %@ AND lastLookedAt > %@ AND folderTypeRawValue IN %@", account,
   3.334 -                earlierTimestamp as CVarArg, FolderType.typesSyncedWithImapServerRawValues)
   3.335 -            let folders = CdFolder.all(predicate: pInteresting) as? [CdFolder] ?? []
   3.336 -            var inboxIsInteresting = false
   3.337 -            var sentFolderIsInteresting = false
   3.338 -            for f in folders {
   3.339 -                if let name = f.name {
   3.340 -                    if f.folderTypeRawValue == FolderType.inbox.rawValue {
   3.341 -                        inboxIsInteresting = true
   3.342 -                    }
   3.343 -                    if f.folderTypeRawValue == FolderType.sent.rawValue {
   3.344 -                        sentFolderIsInteresting = true
   3.345 -                    }
   3.346 -                    folderInfos.append(FolderInfo(
   3.347 -                        name: name, folderType: f.folderType,
   3.348 -                        firstUID: f.firstUID(), lastUID: f.lastUID(), folderID: f.objectID))
   3.349 -                }
   3.350 -            }
   3.351 -            // Try to determine and add inbox and sent folder if not already there. Both are
   3.352 -            // considered as always interesting.
   3.353 -            if !inboxIsInteresting {
   3.354 -                if let inboxFolder = CdFolder.by(folderType: .inbox, account: account) {
   3.355 -                    let name = inboxFolder.name ?? ImapSync.defaultImapInboxName
   3.356 -                    folderInfos.append(FolderInfo(name: name,
   3.357 -                                                  folderType: inboxFolder.folderType,
   3.358 -                                                  firstUID: inboxFolder.firstUID(),
   3.359 -                                                  lastUID: inboxFolder.lastUID(),
   3.360 -                                                  folderID: inboxFolder.objectID))
   3.361 -                }
   3.362 -            }
   3.363 -            // Sent folder must always be interesting. Message Threading relies on this.
   3.364 -            if !sentFolderIsInteresting {
   3.365 -                if let sentFolder = CdFolder.by(folderType: .sent, account: account),
   3.366 -                    let name = sentFolder.name {
   3.367 -                    folderInfos.append(FolderInfo(name: name,
   3.368 -                                                  folderType: sentFolder.folderType,
   3.369 -                                                  firstUID: sentFolder.firstUID(),
   3.370 -                                                  lastUID: sentFolder.lastUID(),
   3.371 -                                                  folderID: sentFolder.objectID))
   3.372 -                }
   3.373 -            }
   3.374 -        }
   3.375 -        if folderInfos.count == 0 {
   3.376 -            // If no interesting folders have been found, at least sync the inbox.
   3.377 -            folderInfos.append(FolderInfo(
   3.378 -                name: ImapSync.defaultImapInboxName, folderType: .inbox, firstUID: nil,
   3.379 -                lastUID: nil, folderID: nil))
   3.380 -        }
   3.381 -        return folderInfos
   3.382 -    }
   3.383 -
   3.384 -    func syncExistingMessagesOperation(folderInfos: [FolderInfo],
   3.385 -                                       errorContainer: ServiceErrorProtocol,
   3.386 -                                       imapSyncData: ImapSyncData,
   3.387 -                                       onlySyncChangesTriggeredByUser: Bool) -> Operation {
   3.388 -        let resultOp = SelfReferencingOperation() { [weak self] operation in
   3.389 -            guard let me = self, let operation = operation else {
   3.390 -                Logger.backendLogger.lostMySelf()
   3.391 -                return
   3.392 -            }
   3.393 -            if operation.isCancelled {
   3.394 -                return
   3.395 -            }
   3.396 -            let queue = OperationQueue()
   3.397 -            var operations = [Operation]()
   3.398 -            var lastOp = Operation()
   3.399 -            operations.append(lastOp)
   3.400 -            MessageModel.performAndWait {
   3.401 -                if operation.isCancelled {
   3.402 -                    return
   3.403 -                }
   3.404 -                for fi in folderInfos {if let folderID = fi.folderID,
   3.405 -                        let firstUID = fi.firstUID,
   3.406 -                        let lastUID = fi.lastUID,
   3.407 -                        firstUID != 0, lastUID != 0, firstUID <= lastUID {
   3.408 -                        if !onlySyncChangesTriggeredByUser {
   3.409 -                            let syncMessagesOp =
   3.410 -                                SyncMessagesOperation(parentName: me.description,
   3.411 -                                                      errorContainer: errorContainer,
   3.412 -                                                      imapSyncData: imapSyncData,
   3.413 -                                                      folderName: fi.name,
   3.414 -                                                      firstUID: firstUID,
   3.415 -                                                      lastUID: lastUID)
   3.416 -                            syncMessagesOp.addDependency(lastOp)
   3.417 -                            lastOp = syncMessagesOp
   3.418 -                            operations.append(syncMessagesOp)
   3.419 -                        }
   3.420 -                        let syncFlagsOp =
   3.421 -                            SyncFlagsToServerOperation(parentName: me.description,
   3.422 -                                                       errorContainer: errorContainer,
   3.423 -                                                       imapSyncData: imapSyncData,
   3.424 -                                                       folderID: folderID)
   3.425 -                        syncFlagsOp.addDependency(lastOp)
   3.426 -                        lastOp = syncFlagsOp
   3.427 -                        operations.append(syncFlagsOp)
   3.428 -                    }
   3.429 -                }
   3.430 -            }
   3.431 -            if operation.isCancelled {
   3.432 -                return
   3.433 -            }
   3.434 -            queue.addOperations(operations, waitUntilFinished: true)
   3.435 -        }
   3.436 -        return resultOp
   3.437 -    }
   3.438 -
   3.439 -    /// Builds a line of opertations to sync one e-mail account.
   3.440 -    ///
   3.441 -    /// - Parameters:
   3.442 -    ///   - accountInfo: Account info for account to sync
   3.443 -    ///   - onlySyncChangesTriggeredByUser: if true, the operation line is build to do just enough
   3.444 -    ///                                     to make sure all user actions (sent, deleted, flagged)
   3.445 -    ///                                     are synced to the server.
   3.446 -    ///                                     If false, changes on server side are synced also.
   3.447 -    /// - Returns: Operation line contaning all operations required to sync one account
   3.448 -    func buildOperationLine(accountInfo: AccountConnectInfo, onlySyncChangesTriggeredByUser: Bool = false) -> OperationLine {
   3.449 -        let errorContainer = ReportingErrorContainer(delegate: self)
   3.450 -        // Operation depending on all IMAP operations for this account
   3.451 -        let opImapFinished = BlockOperation()
   3.452 -        // Operation depending on all SMTP operations for this account
   3.453 -        let opSmtpFinished = BlockOperation()
   3.454 -        // Operation depending on all IMAP and SMTP operations
   3.455 -        let opAllFinished = BlockOperation()
   3.456 -        opAllFinished.addDependency(opSmtpFinished)
   3.457 -        opAllFinished.addDependency(opImapFinished)
   3.458 -
   3.459 -        var operations = [Operation]()
   3.460 -        #if DEBUG
   3.461 -            let debugTimerOp = BlockOperation()
   3.462 -            opAllFinished.addDependency(debugTimerOp)
   3.463 -            operations.append(debugTimerOp)
   3.464 -        #endif
   3.465 -
   3.466 -        if !onlySyncChangesTriggeredByUser {
   3.467 -            let fixAttachmentsOp = FixAttachmentsOperation(parentName: description,
   3.468 -                                                           errorContainer: ErrorContainer())
   3.469 -            operations.append(fixAttachmentsOp)
   3.470 -            opAllFinished.addDependency(fixAttachmentsOp)
   3.471 -        }
   3.472 -
   3.473 -        // Items not associated with any mailbox (e.g., SMTP send)
   3.474 -        let (_, smtpOperations) = buildSmtpOperations(
   3.475 -            accountInfo: accountInfo, errorContainer: ReportingErrorContainer(delegate: self),
   3.476 -            opSmtpFinished: opSmtpFinished, lastOperation: nil)
   3.477 -        operations.append(contentsOf: smtpOperations)
   3.478 -
   3.479 -        if let imapCI = accountInfo.imapConnectInfo {
   3.480 -            let imapSyncData = imapConnectionDataCache.imapConnectionData(for: imapCI)
   3.481 -
   3.482 -            // login IMAP
   3.483 -            let opImapLogin = LoginImapOperation(
   3.484 -                parentName: description, errorContainer: errorContainer,
   3.485 -                imapSyncData: imapSyncData)
   3.486 -            opImapLogin.addDependency(opSmtpFinished)
   3.487 -            opImapFinished.addDependency(opImapLogin)
   3.488 -            operations.append(opImapLogin)
   3.489 -
   3.490 -            var lastImapOp: Operation = opImapLogin
   3.491 -
   3.492 -            if !onlySyncChangesTriggeredByUser {
   3.493 -                // Fetch current list of interesting mailboxes
   3.494 -                if let opSyncFolders = SyncFoldersFromServerOperation(
   3.495 -                    parentName: description,
   3.496 -                    errorContainer: errorContainer,
   3.497 -                    imapSyncData: imapSyncData) {
   3.498 -                    opSyncFolders.completionBlock = {
   3.499 -                        opSyncFolders.completionBlock = nil
   3.500 -                    }
   3.501 -                    opSyncFolders.addDependency(lastImapOp)
   3.502 -                    lastImapOp = opSyncFolders
   3.503 -                    opImapFinished.addDependency(opSyncFolders)
   3.504 -                    operations.append(opSyncFolders)
   3.505 -                }
   3.506 -            }
   3.507 -            if !onlySyncChangesTriggeredByUser {
   3.508 -                let opRequiredFolders = CreateRequiredFoldersOperation(
   3.509 -                    parentName: description, errorContainer: errorContainer,
   3.510 -                    imapSyncData: imapSyncData)
   3.511 -                opRequiredFolders.addDependency(lastImapOp)
   3.512 -                lastImapOp = opRequiredFolders
   3.513 -                opImapFinished.addDependency(opRequiredFolders)
   3.514 -                operations.append(opRequiredFolders)
   3.515 -            }
   3.516 -            // Client-to-server synchronization (IMAP)
   3.517 -            let appendOp = buildAppendOperation(imapSyncData: imapSyncData,
   3.518 -                                                errorContainer: errorContainer)
   3.519 -            appendOp.addDependency(lastImapOp)
   3.520 -            lastImapOp = appendOp
   3.521 -            opImapFinished.addDependency(appendOp)
   3.522 -            operations.append(appendOp)
   3.523 -
   3.524 -            let moveToFolderOp = buildUidMoveToFolderOperation(imapSyncData: imapSyncData,
   3.525 -                                                               errorContainer: errorContainer)
   3.526 -            moveToFolderOp.addDependency(lastImapOp)
   3.527 -            lastImapOp = moveToFolderOp
   3.528 -            opImapFinished.addDependency(moveToFolderOp)
   3.529 -            operations.append(moveToFolderOp)
   3.530 -
   3.531 -            let folderInfos = determineInterestingFolders(accountInfo: accountInfo)
   3.532 -
   3.533 -            // Server-to-client synchronization (IMAP)
   3.534 -            if !onlySyncChangesTriggeredByUser {
   3.535 -                // sync new messages
   3.536 -                for fi in folderInfos {
   3.537 -                    let fetchMessagesOp = FetchMessagesOperation(
   3.538 -                        parentName: description, errorContainer: errorContainer,
   3.539 -                        imapSyncData: imapSyncData, folderName: fi.name) {
   3.540 -                            [weak self] message in self?.messageFetched(cdMessage: message)
   3.541 -                    }
   3.542 -                    fetchMessagesOp.addDependency(lastImapOp)
   3.543 -                    lastImapOp = fetchMessagesOp
   3.544 -                    operations.append(fetchMessagesOp)
   3.545 -                    opImapFinished.addDependency(fetchMessagesOp)
   3.546 -                }
   3.547 -            }
   3.548 -
   3.549 -            if !onlySyncChangesTriggeredByUser {
   3.550 -                let opDecrypt = DecryptMessagesOperation(parentName: description,
   3.551 -                                                         errorContainer: ErrorContainer())
   3.552 -                opDecrypt.addDependency(lastImapOp)
   3.553 -                lastImapOp = opDecrypt
   3.554 -                opImapFinished.addDependency(opDecrypt)
   3.555 -                operations.append(opDecrypt)
   3.556 -                // In case messages need to be re-uploaded (e.g. for trusted server or extra
   3.557 -                // keys), we want do append, fetch and decrypt them in the same sync cycle.
   3.558 -                let opAppendAndFetchReUploaded = SelfReferencingOperation() {
   3.559 -                    [weak self, weak opDecrypt] operation in
   3.560 -                    guard
   3.561 -                        let me = self,
   3.562 -                        let decryptOp = opDecrypt,
   3.563 -                        let operation = operation else {
   3.564 -                            Logger.backendLogger.lostMySelf()
   3.565 -                            return
   3.566 -                    }
   3.567 -                    if operation.isCancelled {
   3.568 -                        return
   3.569 -                    }
   3.570 -                    if !decryptOp.didMarkMessagesForReUpload {
   3.571 -                        // Nothing to do.
   3.572 -                        return
   3.573 -                    }
   3.574 -                    let reUploadQueue = OperationQueue()
   3.575 -                    reUploadQueue.name = "security.pep.networkServiceWorker.ReUploadQueue"
   3.576 -                    var reUploadOperations = [Operation]()
   3.577 -                    var lastOp = Operation()
   3.578 -                    reUploadOperations.append(lastOp)
   3.579 -                    // Append ...
   3.580 -                    let appendOp = me.buildAppendOperation(imapSyncData: imapSyncData,
   3.581 -                                                        errorContainer: errorContainer)
   3.582 -                    appendOp.addDependency(lastOp)
   3.583 -                    lastOp = appendOp
   3.584 -                    reUploadOperations.append(appendOp)
   3.585 -                    // ... fetch ...
   3.586 -                    for fi in folderInfos {
   3.587 -                        let fetchMessagesOp = FetchMessagesOperation(
   3.588 -                            parentName: me.description, errorContainer: errorContainer,
   3.589 -                            imapSyncData: imapSyncData, folderName: fi.name) {
   3.590 -                                [weak self] message in self?.messageFetched(cdMessage: message)
   3.591 -                        }
   3.592 -                        fetchMessagesOp.addDependency(lastOp)
   3.593 -                        lastOp = fetchMessagesOp
   3.594 -                        reUploadOperations.append(fetchMessagesOp)
   3.595 -                    }
   3.596 -                    // ... and decrypt
   3.597 -                    let opDecrypt = DecryptMessagesOperation(parentName: me.description,
   3.598 -                                                             errorContainer: ErrorContainer())
   3.599 -                    opDecrypt.addDependency(lastOp)
   3.600 -                    lastOp = opDecrypt
   3.601 -                    reUploadOperations.append(opDecrypt)
   3.602 -
   3.603 -                    reUploadQueue.addOperations(reUploadOperations, waitUntilFinished: true)
   3.604 -                }
   3.605 -                opAppendAndFetchReUploaded.addDependency(lastImapOp)
   3.606 -                lastImapOp = opAppendAndFetchReUploaded
   3.607 -                opImapFinished.addDependency(opAppendAndFetchReUploaded)
   3.608 -                operations.append(opAppendAndFetchReUploaded)
   3.609 -            }
   3.610 -
   3.611 -            // sync existing messages
   3.612 -            let syncExistingMessagesOP =
   3.613 -                syncExistingMessagesOperation(folderInfos: folderInfos,
   3.614 -                                              errorContainer: errorContainer,
   3.615 -                                              imapSyncData: imapSyncData,
   3.616 -                                              onlySyncChangesTriggeredByUser:
   3.617 -                    onlySyncChangesTriggeredByUser)
   3.618 -            syncExistingMessagesOP.addDependency(lastImapOp)
   3.619 -            lastImapOp = syncExistingMessagesOP
   3.620 -            opImapFinished.addDependency(syncExistingMessagesOP)
   3.621 -            operations.append(syncExistingMessagesOP)
   3.622 -        }
   3.623 -
   3.624 -        operations.append(contentsOf: [opSmtpFinished, opImapFinished, opAllFinished])
   3.625 -
   3.626 -        return OperationLine(accountInfo: accountInfo, operations: operations,
   3.627 -                             finalOperation: opAllFinished, errorContainer: errorContainer)
   3.628 -    }
   3.629 -
   3.630 -    func messageFetched(cdMessage: CdMessage) {
   3.631 -        if cdMessage.imap?.serverFlags?.flagDeleted ?? true == false {
   3.632 -            serviceConfig.sendLayerDelegate?.didFetch(cdMessage: cdMessage)
   3.633 -        }
   3.634 -    }
   3.635 -
   3.636 -    func buildOperationLines(accountConnectInfos: [AccountConnectInfo]) -> [OperationLine] {
   3.637 -        return accountConnectInfos.map {
   3.638 -            return buildOperationLine(accountInfo: $0)
   3.639 -        }
   3.640 -    }
   3.641 -
   3.642 -    func buildSyncLocalChangesOperationLines(accountConnectInfos: [AccountConnectInfo]) -> [OperationLine] {
   3.643 -        return accountConnectInfos.map {
   3.644 -            return buildOperationLine(accountInfo: $0, onlySyncChangesTriggeredByUser: true)
   3.645 -        }
   3.646 -    }
   3.647 -
   3.648 -    func scheduleOperationLine(operationLine: OperationLine, completionBlock: (() -> Void)? = nil) {
   3.649 -        if cancelled{
   3.650 -            return
   3.651 -        }
   3.652 -        var bgID: BackgroundTaskID? = nil
   3.653 -        bgID = serviceConfig.backgrounder?.beginBackgroundTask()
   3.654 -        operationLine.finalOperation.completionBlock = { [weak self, weak operationLine] in
   3.655 -            operationLine?.finalOperation.completionBlock = nil
   3.656 -            self?.serviceConfig.backgrounder?.endBackgroundTask(bgID)
   3.657 -            completionBlock?()
   3.658 -        }
   3.659 -        for op in operationLine.operations {
   3.660 -            if cancelled{
   3.661 -                backgroundQueue.cancelAllOperations()
   3.662 -                return
   3.663 -            }
   3.664 -            backgroundQueue.addOperation(op)
   3.665 -        }
   3.666 -    }
   3.667 -
   3.668 -    func processOperationLines(operationLines: [OperationLine]) {
   3.669 -        if cancelled {
   3.670 -            return
   3.671 -        }
   3.672 -        workerQueue.async { [weak self] in
   3.673 -            guard let me = self else {
   3.674 -                Logger.backendLogger.lostMySelf()
   3.675 -                return
   3.676 -            }
   3.677 -            if me.cancelled {
   3.678 -                return
   3.679 -            }
   3.680 -            me.processOperationLinesInternal(operationLines: operationLines)
   3.681 -        }
   3.682 -    }
   3.683 -
   3.684 -    func processOperationLinesInternal(operationLines: [OperationLine], repeatProcess: Bool = true) {
   3.685 -        if !self.cancelled {
   3.686 -            var myLines = operationLines
   3.687 -            if myLines.first != nil {
   3.688 -                let ol = myLines.removeFirst()
   3.689 -                scheduleOperationLine(operationLine: ol, completionBlock: {
   3.690 -                    [weak self] in
   3.691 -                    guard let me = self else {
   3.692 -                        return
   3.693 -                    }
   3.694 -                    // UNIT TEST ONLY
   3.695 -                    me.unitTestDelegate?
   3.696 -                        .testWorkerDidSync(worker: me,
   3.697 -                                           accountInfo: ol.accountInfo,
   3.698 -                                           errorProtocol: ol.errorContainer)
   3.699 -                    // Process the rest
   3.700 -                    me.processOperationLines(operationLines: myLines)
   3.701 -                })
   3.702 -            } else {
   3.703 -                workerQueue.asyncAfter(deadline: DispatchTime.now() +
   3.704 -                    self.serviceConfig.sleepTimeInSeconds) { [weak self] in
   3.705 -                        guard let strongSelf = self else {
   3.706 -                            return
   3.707 -                        }
   3.708 -                        if repeatProcess && !strongSelf.cancelled{
   3.709 -                            strongSelf.processAllInternal()
   3.710 -                        }
   3.711 -                }
   3.712 -            }
   3.713 -        }
   3.714 -    }
   3.715 -}
   3.716 -
   3.717 -// MARK: - ReportingErrorContainerDelegate
   3.718 -
   3.719 -extension NetworkServiceWorker: ReportingErrorContainerDelegate {
   3.720 -    public func reportingErrorContainer(_ errorContainer: ReportingErrorContainer, didReceive error: Error) {
   3.721 -        delegate?.networkServiceWorker(self, errorOccured: error)
   3.722 -    }
   3.723 -}
   3.724 -
   3.725 -// MARK: - CustomStringConvertible
   3.726 -
   3.727 -extension NetworkServiceWorker: CustomStringConvertible {
   3.728 -    public var description: String {
   3.729 -        let parentDescription = serviceConfig.parentName
   3.730 -        let ref = unsafeBitCast(self, to: UnsafeRawPointer.self)
   3.731 -        return "NetworkServiceWorker \(ref) (\(parentDescription))"
   3.732 -    }
   3.733 -}
   3.734 -
   3.735 -// MARK: - UNIT TEST ONLY
   3.736 -
   3.737 -protocol NetworkServiceWorkerUnitTestDelegate: class {
   3.738 -    /** Called after each account sync */
   3.739 -    func testWorkerDidSync(worker: NetworkServiceWorker,
   3.740 -                                     accountInfo: AccountConnectInfo,
   3.741 -                                     errorProtocol: ServiceErrorProtocol)
   3.742 -}