Merge from default IOS-1110
authorMiguel Berrocal Go?mez <miguel@helm.cat>
Wed, 04 Jul 2018 11:27:58 +0200
branchIOS-1110
changeset 5235aeb0b325c8d2
parent 5234 aab2cb36b01b
parent 5218 c0b39f4a0d54
child 5237 f3a162b5669c
Merge from default
pEpForiOS.xcodeproj/project.pbxproj
pEpForiOS/Background/AppendDraftMailsOperation.swift
pEpForiOS/Background/AppendMailsOperationBase.swift
pEpForiOS/Background/AppendSendMailsOperation.swift
pEpForiOS/Network/Service/MessageSyncService/AtomicImapService.swift
pEpForiOS/Network/Service/MessageSyncService/BackgroundOperationImapService.swift
pEpForiOS/Network/Service/MessageSyncService/FetchMessagesService.swift
pEpForiOS/Network/Service/MessageSyncService/ImapIdleService.swift
pEpForiOS/Network/Service/MessageSyncService/ImapSmtpConnection.swift
pEpForiOS/Network/Service/MessageSyncService/ImapSmtpSyncService.swift
pEpForiOS/Network/Service/MessageSyncService/ServiceChainExecutor.swift
pEpForiOS/Network/Service/MessageSyncService/ServiceFactory.swift
pEpForiOS/Network/Service/MessageSyncService/SmtpSendService.swift
pEpForiOS/Network/Service/MessageSyncService/SyncExistingMessagesService.swift
pEpForiOS/Network/Service/MessageSyncService/SyncFlagsToServerService.swift
pEpForiOS/Network/Service/MessageSyncService/SyncFoldersFromServerService.swift
pEpForiOS/UI/EmailDisplay/EmailViewController.swift
pEpForiOSTests/FetchFoldersServiceTests.swift
pEpForiOSTests/FetchMessagesServiceTests.swift
pEpForiOSTests/MessageSyncServiceTests.swift
pEpForiOSTests/ServiceChainExecutorTests.swift
pEpForiOSTests/ServiceFactoryTests.swift
pEpForiOSTests/SmtpSendServiceTests.swift
pEpForiOSTests/SyncExistingMessagesServiceTests.swift
     1.1 --- a/pEpForiOS.xcodeproj/project.pbxproj	Wed Jul 04 11:24:51 2018 +0200
     1.2 +++ b/pEpForiOS.xcodeproj/project.pbxproj	Wed Jul 04 11:27:58 2018 +0200
     1.3 @@ -59,7 +59,6 @@
     1.4  		153CA6E41FB60D99003C9629 /* DercyptMessagesOperationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153CA6E31FB60D99003C9629 /* DercyptMessagesOperationTest.swift */; };
     1.5  		153CA6E81FB61B53003C9629 /* IOS-815_pep_rating_zero.txt in Resources */ = {isa = PBXBuildFile; fileRef = 153CA6E71FB61B53003C9629 /* IOS-815_pep_rating_zero.txt */; };
     1.6  		153CA6EA1FB62195003C9629 /* IOS-211-pdfEmail.txt in Resources */ = {isa = PBXBuildFile; fileRef = 153CA6E91FB62194003C9629 /* IOS-211-pdfEmail.txt */; };
     1.7 -		153D08281F56BBCB00377110 /* AppendSendMailsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153D08271F56BBCB00377110 /* AppendSendMailsOperation.swift */; };
     1.8  		153E4E471F500F650021CBD8 /* IOS-211-duplicated-attachments.txt in Resources */ = {isa = PBXBuildFile; fileRef = 153E4E461F500F650021CBD8 /* IOS-211-duplicated-attachments.txt */; };
     1.9  		153FC45D202A263D0053CCF1 /* FolderType+IMAP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153FC45C202A263D0053CCF1 /* FolderType+IMAP.swift */; };
    1.10  		153FC45F202A26B30053CCF1 /* FolderType+IMAPTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153FC45E202A26B30053CCF1 /* FolderType+IMAPTest.swift */; };
    1.11 @@ -84,6 +83,7 @@
    1.12  		1560D1701F6FC99B00A75B39 /* FetchOlderImapMessagesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1560D16F1F6FC99B00A75B39 /* FetchOlderImapMessagesOperation.swift */; };
    1.13  		1560D1721F6FD1F600A75B39 /* FetchOlderImapMessagesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1560D1711F6FD1F600A75B39 /* FetchOlderImapMessagesService.swift */; };
    1.14  		1568FEA11FACDCC600993EA3 /* IOS-211_hi_there.txt in Resources */ = {isa = PBXBuildFile; fileRef = 1568FEA01FACDCC500993EA3 /* IOS-211_hi_there.txt */; };
    1.15 +		1569AEA520E14DC3002102A0 /* ReUploadTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1569AEA420E14DC2002102A0 /* ReUploadTest.swift */; };
    1.16  		157455C81FDFD2D4008CA78F /* NSAttributedString+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157455C71FDFD2D4008CA78F /* NSAttributedString+Extensions.swift */; };
    1.17  		1579397B1F4E00AF00A2A6CF /* UINavigationController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1579397A1F4E00AF00A2A6CF /* UINavigationController+Extensions.swift */; };
    1.18  		15865A8920319ADC00F7A4B5 /* Folder+pEp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15865A8820319ADC00F7A4B5 /* Folder+pEp.swift */; };
    1.19 @@ -110,6 +110,10 @@
    1.20  		15BBBC6C1FD05F4300B9DCC8 /* DisplayUserError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 152130531FD00B7A00688DF2 /* DisplayUserError.swift */; };
    1.21  		15C5F2441F822560007DE086 /* PreviewMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15C5F2431F822560007DE086 /* PreviewMessage.swift */; };
    1.22  		15C5F2461F823752007DE086 /* SortedSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15C5F2451F823752007DE086 /* SortedSet.swift */; };
    1.23 +		15EBE43B20E5286500268859 /* unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_sec.asc in Resources */ = {isa = PBXBuildFile; fileRef = 15EBE43920E5286500268859 /* unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_sec.asc */; };
    1.24 +		15EBE43E20E5296900268859 /* unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_pub.asc in Resources */ = {isa = PBXBuildFile; fileRef = 15EBE43C20E5296900268859 /* unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_pub.asc */; };
    1.25 +		15EBE44220E5353E00268859 /* unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_pub.asc in Resources */ = {isa = PBXBuildFile; fileRef = 15EBE44020E5353D00268859 /* unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_pub.asc */; };
    1.26 +		15EBE44320E5353E00268859 /* unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_sec.asc in Resources */ = {isa = PBXBuildFile; fileRef = 15EBE44120E5353D00268859 /* unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_sec.asc */; };
    1.27  		15EE5A75203B15670041F076 /* SettingBaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15EE5A72203B15660041F076 /* SettingBaseViewController.swift */; };
    1.28  		15EE5A76203B15670041F076 /* LogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15EE5A73203B15660041F076 /* LogViewController.swift */; };
    1.29  		15F835241F34BE1300FCE887 /* AccountUserInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F835231F34BE1300FCE887 /* AccountUserInput.swift */; };
    1.30 @@ -133,7 +137,6 @@
    1.31  		222B35861DF97A44007A1F82 /* ComposeDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222B35821DF97A44007A1F82 /* ComposeDataSource.swift */; };
    1.32  		222B35BC1E00049C007A1F82 /* AccountCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222B35BB1E00049C007A1F82 /* AccountCell.swift */; };
    1.33  		228038681DC9DE6D00F1CB45 /* TextfieldResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228038671DC9DE6D00F1CB45 /* TextfieldResponder.swift */; };
    1.34 -		4301B3161E28D259007D626C /* AppendMailsOperationBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4301B3151E28D259007D626C /* AppendMailsOperationBase.swift */; };
    1.35  		43040A531E9776220083DED8 /* AttachmentSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43040A521E9776220083DED8 /* AttachmentSummaryView.swift */; };
    1.36  		4304FD001EBB8EBB0086DADA /* LanguageListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4304FCFE1EBB8C5A0086DADA /* LanguageListViewController.swift */; };
    1.37  		4307C4701ED81F3100A276A4 /* DefaultImapSyncDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4307C46F1ED81F3100A276A4 /* DefaultImapSyncDelegate.swift */; };
    1.38 @@ -148,8 +151,6 @@
    1.39  		430D73671E9CC54000EA6FA9 /* AttachmentToLocalURLOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430D73661E9CC54000EA6FA9 /* AttachmentToLocalURLOperation.swift */; };
    1.40  		430E0BE71EAF5E2600378EC2 /* NSMutableDictionary+pEp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430E0BE61EAF5E2600378EC2 /* NSMutableDictionary+pEp.swift */; };
    1.41  		430E5F201EBC87A700E5D5D3 /* LanguageListTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430E5F1F1EBC87A700E5D5D3 /* LanguageListTableViewCell.swift */; };
    1.42 -		430EA5FA1F0FAD7700F816D4 /* ServiceChainExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430EA5F91F0FAD7700F816D4 /* ServiceChainExecutor.swift */; };
    1.43 -		430EA5FC1F0FB92A00F816D4 /* ServiceChainExecutorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430EA5FB1F0FB92A00F816D4 /* ServiceChainExecutorTests.swift */; };
    1.44  		43106A192045716000693144 /* OAuth2ConfigurationProtocol+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43106A182045716000693144 /* OAuth2ConfigurationProtocol+Extension.swift */; };
    1.45  		431144B51CC0FCA40007639D /* StoreFolderOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431144B41CC0FCA40007639D /* StoreFolderOperation.swift */; };
    1.46  		431144B71CC11D6A0007639D /* BaseOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431144B61CC11D6A0007639D /* BaseOperation.swift */; };
    1.47 @@ -181,15 +182,12 @@
    1.48  		431D60DB1E93BB2D001266D7 /* AttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431D60DA1E93BB2D001266D7 /* AttachmentsView.swift */; };
    1.49  		431D60DD1E93D580001266D7 /* MessageAttachmentsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431D60DC1E93D580001266D7 /* MessageAttachmentsCell.swift */; };
    1.50  		431E2B071F02550C000035FA /* CheckOutgoingMessagesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E2B061F02550C000035FA /* CheckOutgoingMessagesOperation.swift */; };
    1.51 -		431E2B091F0281DF000035FA /* SmtpSendService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E2B081F0281DF000035FA /* SmtpSendService.swift */; };
    1.52  		431E58F61ED57F6500EFA77F /* AccountVerificationServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E58F51ED57F6500EFA77F /* AccountVerificationServiceProtocol.swift */; };
    1.53  		431E58FA1ED591E900EFA77F /* AccountVerificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E58F91ED591E900EFA77F /* AccountVerificationService.swift */; };
    1.54  		431E58FC1ED5926B00EFA77F /* AccountVerificationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E58FB1ED5926B00EFA77F /* AccountVerificationServiceTests.swift */; };
    1.55  		431E65631EEAE65200B8BBFC /* HandshakeUITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E65621EEAE65200B8BBFC /* HandshakeUITest.swift */; };
    1.56  		431E8F7E1CFDCF3A00C33647 /* EmailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431E8F7D1CFDCF3A00C33647 /* EmailViewController.swift */; };
    1.57  		431F987F1F6FD3E300A1E4D2 /* HandshakePartnerTableViewCellViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431F987E1F6FD3E300A1E4D2 /* HandshakePartnerTableViewCellViewModelTests.swift */; };
    1.58 -		43200D9D1F0CBC4000FFDE56 /* FetchMessagesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43200D9C1F0CBC4000FFDE56 /* FetchMessagesService.swift */; };
    1.59 -		43200D9F1F0CD81300FFDE56 /* FetchMessagesServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43200D9E1F0CD81300FFDE56 /* FetchMessagesServiceTests.swift */; };
    1.60  		43209B541ECC5A9B007E7E2E /* libpEpObjCAdapter.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 43209B531ECC5A9B007E7E2E /* libpEpObjCAdapter.a */; };
    1.61  		432142641E8FD66900FBE987 /* FetchNumberOfNewMailsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432142631E8FD66900FBE987 /* FetchNumberOfNewMailsService.swift */; };
    1.62  		432142661E8FD6A400FBE987 /* ServiceUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432142651E8FD6A400FBE987 /* ServiceUtil.swift */; };
    1.63 @@ -204,12 +202,9 @@
    1.64  		43257C891F50683600DDC7F0 /* NSAttributedString+pEp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43257C871F5067BE00DDC7F0 /* NSAttributedString+pEp.swift */; };
    1.65  		432645811F4C26CF002E3EF8 /* NSAttributedString+Parsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432645801F4C26CF002E3EF8 /* NSAttributedString+Parsing.swift */; };
    1.66  		43264E9B1D76B7110098DCAC /* SyncFlagsToServerOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43264E9A1D76B7110098DCAC /* SyncFlagsToServerOperation.swift */; };
    1.67 -		432677161F17796D00F01F5A /* ImapIdleService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432677151F17796D00F01F5A /* ImapIdleService.swift */; };
    1.68  		4326D3FF1EFBC8DB0016AB0D /* FolderInfoOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4326D3FE1EFBC8DB0016AB0D /* FolderInfoOperation.swift */; };
    1.69  		43293EFB1EB9DD6700EEE010 /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43293EFA1EB9DD6700EEE010 /* UIViewController+Extension.swift */; };
    1.70  		432A24D71DE714A200DAAC5C /* MessagePantomimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432A24D61DE714A200DAAC5C /* MessagePantomimeTests.swift */; };
    1.71 -		432A3E3F1F0E6B3700834749 /* SyncExistingMessagesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432A3E3E1F0E6B3700834749 /* SyncExistingMessagesService.swift */; };
    1.72 -		432A3E441F0E793A00834749 /* SyncExistingMessagesServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432A3E431F0E793A00834749 /* SyncExistingMessagesServiceTests.swift */; };
    1.73  		432A5E261EB344C3007CB670 /* PEP_color+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432A5E251EB344C3007CB670 /* PEP_color+Extension.swift */; };
    1.74  		432AC3061ECB0C44007DC418 /* CWIMAPMessage+pEp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432AC3051ECB0C44007DC418 /* CWIMAPMessage+pEp.swift */; };
    1.75  		432DA7441EE01E3300B30BAA /* MessageSyncServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432DA7431EE01E3300B30BAA /* MessageSyncServiceProtocol.swift */; };
    1.76 @@ -275,7 +270,6 @@
    1.77  		4354E5EF209B07790030304B /* TextViewInTableViewScrollUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4354E5EE209B07790030304B /* TextViewInTableViewScrollUtil.swift */; };
    1.78  		4356102C1DEF019400808C8E /* SyncMessagesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4356102B1DEF019400808C8E /* SyncMessagesOperation.swift */; };
    1.79  		435B42411D211E5900119048 /* MiscUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435B42401D211E5900119048 /* MiscUtil.swift */; };
    1.80 -		435DF9091F2A26F2003254F7 /* BackgroundOperationImapService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435DF9081F2A26F2003254F7 /* BackgroundOperationImapService.swift */; };
    1.81  		4360282D1ED6F27600C95FC4 /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4360282C1ED6F27600C95FC4 /* NetworkError.swift */; };
    1.82  		4360282F1ED6F33400C95FC4 /* ImapSyncError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4360282E1ED6F33400C95FC4 /* ImapSyncError.swift */; };
    1.83  		4362398C1EADD61B00BD2EB9 /* CGSize+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4362398B1EADD61B00BD2EB9 /* CGSize+Extension.swift */; };
    1.84 @@ -373,23 +367,17 @@
    1.85  		43C3B16620038B2500ED48A4 /* IOS-884_001_iostest002@peptest.ch.pub.key in Resources */ = {isa = PBXBuildFile; fileRef = 43C3B16320038B2500ED48A4 /* IOS-884_001_iostest002@peptest.ch.pub.key */; };
    1.86  		43C3B16720038B2500ED48A4 /* IOS-884_001_iostest002@peptest.ch.sec.key in Resources */ = {isa = PBXBuildFile; fileRef = 43C3B16420038B2500ED48A4 /* IOS-884_001_iostest002@peptest.ch.sec.key */; };
    1.87  		43C3B16820038B2500ED48A4 /* IOS-884_001_test010@peptest.ch.pub.key in Resources */ = {isa = PBXBuildFile; fileRef = 43C3B16520038B2500ED48A4 /* IOS-884_001_test010@peptest.ch.pub.key */; };
    1.88 -		43C579E91F0A1FEC00A8EDF0 /* AtomicImapService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C579E81F0A1FEC00A8EDF0 /* AtomicImapService.swift */; };
    1.89 -		43C579EB1F0A382400A8EDF0 /* SyncFoldersFromServerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C579EA1F0A382400A8EDF0 /* SyncFoldersFromServerService.swift */; };
    1.90 -		43C579ED1F0A465000A8EDF0 /* FetchFoldersServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C579EC1F0A465000A8EDF0 /* FetchFoldersServiceTests.swift */; };
    1.91  		43C6F35B1CBE7496006A2A18 /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C6F35A1CBE7496006A2A18 /* ConnectionManager.swift */; };
    1.92  		43C7B9D11CEC4DDF007A612F /* MiscTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C7B9D01CEC4DDF007A612F /* MiscTests.swift */; };
    1.93  		43CE63C51DE87FB200FAC505 /* Identity+pEp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE63C41DE87FB200FAC505 /* Identity+pEp.swift */; };
    1.94  		43CE63CB1DE8830100FAC505 /* CdAccount+pEp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE63CA1DE8830100FAC505 /* CdAccount+pEp.swift */; };
    1.95  		43CE63D11DE8866C00FAC505 /* Message+pEp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE63D01DE8866C00FAC505 /* Message+pEp.swift */; };
    1.96 -		43D2C2681F13847700C97235 /* ServiceFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D2C2671F13847700C97235 /* ServiceFactory.swift */; };
    1.97 -		43D2C26A1F1385CF00C97235 /* ServiceFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D2C2691F1385CF00C97235 /* ServiceFactoryTests.swift */; };
    1.98  		43D51E891DD5D902008B77A8 /* SimpleOperationsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D51E881DD5D902008B77A8 /* SimpleOperationsTest.swift */; };
    1.99  		43D755EB1F25D234006F933A /* MatchUidToMsnOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D755EA1F25D234006F933A /* MatchUidToMsnOperation.swift */; };
   1.100  		43D755F61F262B37006F933A /* PantomimeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D755F51F262B37006F933A /* PantomimeError.swift */; };
   1.101 -		43D755F81F262FA0006F933A /* SyncFlagsToServerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D755F71F262FA0006F933A /* SyncFlagsToServerService.swift */; };
   1.102  		43D755FC1F26382B006F933A /* EmailConnectInfo+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D755FB1F26382B006F933A /* EmailConnectInfo+Extension.swift */; };
   1.103  		43DA52681CEF1B4F0023D540 /* NewAccountSetupUITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DA52671CEF1B4F0023D540 /* NewAccountSetupUITest.swift */; };
   1.104 -		43DB81331E2A56BE00A20902 /* AppendDraftMailsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DB81321E2A56BE00A20902 /* AppendDraftMailsOperation.swift */; };
   1.105 +		43DB81331E2A56BE00A20902 /* AppendMailsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DB81321E2A56BE00A20902 /* AppendMailsOperation.swift */; };
   1.106  		43DFB0331E36083D00175C9C /* MessageHeapBufferOverflow.txt in Resources */ = {isa = PBXBuildFile; fileRef = 43DFB0321E36083D00175C9C /* MessageHeapBufferOverflow.txt */; };
   1.107  		43E0CA2A1F4AB81600D9BB7E /* Attachment+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E0CA291F4AB81600D9BB7E /* Attachment+Extension.swift */; };
   1.108  		43E1619120D7B2D6003F1514 /* UpdateThreadListDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E1619020D7B2D6003F1514 /* UpdateThreadListDelegate.swift */; };
   1.109 @@ -425,10 +413,6 @@
   1.110  		43FAA0D21EC9972B005BFC4B /* Tuple.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FAA0D11EC9972B005BFC4B /* Tuple.swift */; };
   1.111  		43FAA0D41EC9CBC0005BFC4B /* DecryptionTestsInternal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FAA0D31EC9CBC0005BFC4B /* DecryptionTestsInternal.swift */; };
   1.112  		43FC02121F4D913D00273304 /* NSHTML_2017-08-09 15_40_53 +0000.html in Resources */ = {isa = PBXBuildFile; fileRef = 43FC02111F4D913D00273304 /* NSHTML_2017-08-09 15_40_53 +0000.html */; };
   1.113 -		43FC24211F04F3F400C32110 /* ImapSmtpSyncService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FC24201F04F3F400C32110 /* ImapSmtpSyncService.swift */; };
   1.114 -		43FC24231F04F43000C32110 /* ImapSmtpConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FC24221F04F43000C32110 /* ImapSmtpConnection.swift */; };
   1.115 -		43FC24251F04FC6E00C32110 /* MessageSyncServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FC24241F04FC6E00C32110 /* MessageSyncServiceTests.swift */; };
   1.116 -		43FC24271F04FDDF00C32110 /* SmtpSendServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FC24261F04FDDF00C32110 /* SmtpSendServiceTests.swift */; };
   1.117  		43FE802E209992B800E97AB3 /* QualifyServerIsLocalService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FE802D209992B800E97AB3 /* QualifyServerIsLocalService.swift */; };
   1.118  		43FE8030209995AD00E97AB3 /* QualifyServerIsLocalServiceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FE802F209995AD00E97AB3 /* QualifyServerIsLocalServiceTest.swift */; };
   1.119  		43FE80322099F62400E97AB3 /* SubjectComposeTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FE80312099F62400E97AB3 /* SubjectComposeTextView.swift */; };
   1.120 @@ -578,7 +562,6 @@
   1.121  		153CA6E31FB60D99003C9629 /* DercyptMessagesOperationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DercyptMessagesOperationTest.swift; sourceTree = "<group>"; };
   1.122  		153CA6E71FB61B53003C9629 /* IOS-815_pep_rating_zero.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "IOS-815_pep_rating_zero.txt"; sourceTree = "<group>"; };
   1.123  		153CA6E91FB62194003C9629 /* IOS-211-pdfEmail.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "IOS-211-pdfEmail.txt"; sourceTree = "<group>"; };
   1.124 -		153D08271F56BBCB00377110 /* AppendSendMailsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppendSendMailsOperation.swift; sourceTree = "<group>"; };
   1.125  		153E4E461F500F650021CBD8 /* IOS-211-duplicated-attachments.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "IOS-211-duplicated-attachments.txt"; sourceTree = "<group>"; };
   1.126  		153FC45C202A263D0053CCF1 /* FolderType+IMAP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FolderType+IMAP.swift"; sourceTree = "<group>"; };
   1.127  		153FC45E202A26B30053CCF1 /* FolderType+IMAPTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FolderType+IMAPTest.swift"; sourceTree = "<group>"; };
   1.128 @@ -603,6 +586,7 @@
   1.129  		1560D16F1F6FC99B00A75B39 /* FetchOlderImapMessagesOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchOlderImapMessagesOperation.swift; sourceTree = "<group>"; };
   1.130  		1560D1711F6FD1F600A75B39 /* FetchOlderImapMessagesService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchOlderImapMessagesService.swift; sourceTree = "<group>"; };
   1.131  		1568FEA01FACDCC500993EA3 /* IOS-211_hi_there.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "IOS-211_hi_there.txt"; sourceTree = "<group>"; };
   1.132 +		1569AEA420E14DC2002102A0 /* ReUploadTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReUploadTest.swift; sourceTree = "<group>"; };
   1.133  		157455C71FDFD2D4008CA78F /* NSAttributedString+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Extensions.swift"; sourceTree = "<group>"; };
   1.134  		1579397A1F4E00AF00A2A6CF /* UINavigationController+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Extensions.swift"; sourceTree = "<group>"; };
   1.135  		15865A8820319ADC00F7A4B5 /* Folder+pEp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+pEp.swift"; sourceTree = "<group>"; };
   1.136 @@ -627,6 +611,10 @@
   1.137  		15BBBC6A1FD0527200B9DCC8 /* DisplayUserErrorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayUserErrorTest.swift; sourceTree = "<group>"; };
   1.138  		15C5F2431F822560007DE086 /* PreviewMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewMessage.swift; sourceTree = "<group>"; };
   1.139  		15C5F2451F823752007DE086 /* SortedSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortedSet.swift; sourceTree = "<group>"; };
   1.140 +		15EBE43920E5286500268859 /* unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_sec.asc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_sec.asc; sourceTree = "<group>"; };
   1.141 +		15EBE43C20E5296900268859 /* unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_pub.asc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_pub.asc; sourceTree = "<group>"; };
   1.142 +		15EBE44020E5353D00268859 /* unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_pub.asc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_pub.asc; sourceTree = "<group>"; };
   1.143 +		15EBE44120E5353D00268859 /* unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_sec.asc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_sec.asc; sourceTree = "<group>"; };
   1.144  		15EE5A72203B15660041F076 /* SettingBaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingBaseViewController.swift; sourceTree = "<group>"; };
   1.145  		15EE5A73203B15660041F076 /* LogViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogViewController.swift; sourceTree = "<group>"; };
   1.146  		15F835231F34BE1300FCE887 /* AccountUserInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountUserInput.swift; sourceTree = "<group>"; };
   1.147 @@ -649,7 +637,6 @@
   1.148  		222B35821DF97A44007A1F82 /* ComposeDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComposeDataSource.swift; sourceTree = "<group>"; };
   1.149  		222B35BB1E00049C007A1F82 /* AccountCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountCell.swift; sourceTree = "<group>"; };
   1.150  		228038671DC9DE6D00F1CB45 /* TextfieldResponder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextfieldResponder.swift; sourceTree = "<group>"; };
   1.151 -		4301B3151E28D259007D626C /* AppendMailsOperationBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppendMailsOperationBase.swift; sourceTree = "<group>"; };
   1.152  		43040A521E9776220083DED8 /* AttachmentSummaryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentSummaryView.swift; sourceTree = "<group>"; };
   1.153  		4304FCFE1EBB8C5A0086DADA /* LanguageListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LanguageListViewController.swift; sourceTree = "<group>"; };
   1.154  		4307C46F1ED81F3100A276A4 /* DefaultImapSyncDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DefaultImapSyncDelegate.swift; path = ../DefaultImapSyncDelegate.swift; sourceTree = "<group>"; };
   1.155 @@ -663,8 +650,6 @@
   1.156  		430D73661E9CC54000EA6FA9 /* AttachmentToLocalURLOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentToLocalURLOperation.swift; sourceTree = "<group>"; };
   1.157  		430E0BE61EAF5E2600378EC2 /* NSMutableDictionary+pEp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSMutableDictionary+pEp.swift"; sourceTree = "<group>"; };
   1.158  		430E5F1F1EBC87A700E5D5D3 /* LanguageListTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LanguageListTableViewCell.swift; sourceTree = "<group>"; };
   1.159 -		430EA5F91F0FAD7700F816D4 /* ServiceChainExecutor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceChainExecutor.swift; sourceTree = "<group>"; };
   1.160 -		430EA5FB1F0FB92A00F816D4 /* ServiceChainExecutorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceChainExecutorTests.swift; sourceTree = "<group>"; };
   1.161  		43106A182045716000693144 /* OAuth2ConfigurationProtocol+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OAuth2ConfigurationProtocol+Extension.swift"; sourceTree = "<group>"; };
   1.162  		431144B41CC0FCA40007639D /* StoreFolderOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreFolderOperation.swift; sourceTree = "<group>"; };
   1.163  		431144B61CC11D6A0007639D /* BaseOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseOperation.swift; sourceTree = "<group>"; };
   1.164 @@ -697,15 +682,12 @@
   1.165  		431D60DA1E93BB2D001266D7 /* AttachmentsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentsView.swift; sourceTree = "<group>"; };
   1.166  		431D60DC1E93D580001266D7 /* MessageAttachmentsCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageAttachmentsCell.swift; sourceTree = "<group>"; };
   1.167  		431E2B061F02550C000035FA /* CheckOutgoingMessagesOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckOutgoingMessagesOperation.swift; sourceTree = "<group>"; };
   1.168 -		431E2B081F0281DF000035FA /* SmtpSendService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SmtpSendService.swift; sourceTree = "<group>"; };
   1.169  		431E58F51ED57F6500EFA77F /* AccountVerificationServiceProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountVerificationServiceProtocol.swift; sourceTree = "<group>"; };
   1.170  		431E58F91ED591E900EFA77F /* AccountVerificationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountVerificationService.swift; sourceTree = "<group>"; };
   1.171  		431E58FB1ED5926B00EFA77F /* AccountVerificationServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountVerificationServiceTests.swift; sourceTree = "<group>"; };
   1.172  		431E65621EEAE65200B8BBFC /* HandshakeUITest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HandshakeUITest.swift; sourceTree = "<group>"; };
   1.173  		431E8F7D1CFDCF3A00C33647 /* EmailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailViewController.swift; sourceTree = "<group>"; };
   1.174  		431F987E1F6FD3E300A1E4D2 /* HandshakePartnerTableViewCellViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HandshakePartnerTableViewCellViewModelTests.swift; sourceTree = "<group>"; };
   1.175 -		43200D9C1F0CBC4000FFDE56 /* FetchMessagesService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchMessagesService.swift; sourceTree = "<group>"; };
   1.176 -		43200D9E1F0CD81300FFDE56 /* FetchMessagesServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchMessagesServiceTests.swift; sourceTree = "<group>"; };
   1.177  		43209B531ECC5A9B007E7E2E /* libpEpObjCAdapter.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpEpObjCAdapter.a; path = "../pEpObjCAdapter/build/Debug-iphoneos/libpEpObjCAdapter.a"; sourceTree = "<group>"; };
   1.178  		432142631E8FD66900FBE987 /* FetchNumberOfNewMailsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchNumberOfNewMailsService.swift; sourceTree = "<group>"; };
   1.179  		432142651E8FD6A400FBE987 /* ServiceUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceUtil.swift; sourceTree = "<group>"; };
   1.180 @@ -720,12 +702,9 @@
   1.181  		43257C871F5067BE00DDC7F0 /* NSAttributedString+pEp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+pEp.swift"; sourceTree = "<group>"; };
   1.182  		432645801F4C26CF002E3EF8 /* NSAttributedString+Parsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Parsing.swift"; sourceTree = "<group>"; };
   1.183  		43264E9A1D76B7110098DCAC /* SyncFlagsToServerOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncFlagsToServerOperation.swift; sourceTree = "<group>"; };
   1.184 -		432677151F17796D00F01F5A /* ImapIdleService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImapIdleService.swift; sourceTree = "<group>"; };
   1.185  		4326D3FE1EFBC8DB0016AB0D /* FolderInfoOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FolderInfoOperation.swift; sourceTree = "<group>"; };
   1.186  		43293EFA1EB9DD6700EEE010 /* UIViewController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = "<group>"; };
   1.187  		432A24D61DE714A200DAAC5C /* MessagePantomimeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagePantomimeTests.swift; sourceTree = "<group>"; };
   1.188 -		432A3E3E1F0E6B3700834749 /* SyncExistingMessagesService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncExistingMessagesService.swift; sourceTree = "<group>"; };
   1.189 -		432A3E431F0E793A00834749 /* SyncExistingMessagesServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncExistingMessagesServiceTests.swift; sourceTree = "<group>"; };
   1.190  		432A5E251EB344C3007CB670 /* PEP_color+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PEP_color+Extension.swift"; sourceTree = "<group>"; };
   1.191  		432AC3051ECB0C44007DC418 /* CWIMAPMessage+pEp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CWIMAPMessage+pEp.swift"; sourceTree = "<group>"; };
   1.192  		432DA7431EE01E3300B30BAA /* MessageSyncServiceProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageSyncServiceProtocol.swift; sourceTree = "<group>"; };
   1.193 @@ -811,7 +790,6 @@
   1.194  		4354E5EE209B07790030304B /* TextViewInTableViewScrollUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewInTableViewScrollUtil.swift; sourceTree = "<group>"; };
   1.195  		4356102B1DEF019400808C8E /* SyncMessagesOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncMessagesOperation.swift; sourceTree = "<group>"; };
   1.196  		435B42401D211E5900119048 /* MiscUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MiscUtil.swift; sourceTree = "<group>"; };
   1.197 -		435DF9081F2A26F2003254F7 /* BackgroundOperationImapService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundOperationImapService.swift; sourceTree = "<group>"; };
   1.198  		4360282C1ED6F27600C95FC4 /* NetworkError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = "<group>"; };
   1.199  		4360282E1ED6F33400C95FC4 /* ImapSyncError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImapSyncError.swift; sourceTree = "<group>"; };
   1.200  		4362398B1EADD61B00BD2EB9 /* CGSize+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGSize+Extension.swift"; sourceTree = "<group>"; };
   1.201 @@ -917,23 +895,17 @@
   1.202  		43C3B16320038B2500ED48A4 /* IOS-884_001_iostest002@peptest.ch.pub.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "IOS-884_001_iostest002@peptest.ch.pub.key"; sourceTree = "<group>"; };
   1.203  		43C3B16420038B2500ED48A4 /* IOS-884_001_iostest002@peptest.ch.sec.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "IOS-884_001_iostest002@peptest.ch.sec.key"; sourceTree = "<group>"; };
   1.204  		43C3B16520038B2500ED48A4 /* IOS-884_001_test010@peptest.ch.pub.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "IOS-884_001_test010@peptest.ch.pub.key"; sourceTree = "<group>"; };
   1.205 -		43C579E81F0A1FEC00A8EDF0 /* AtomicImapService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AtomicImapService.swift; sourceTree = "<group>"; };
   1.206 -		43C579EA1F0A382400A8EDF0 /* SyncFoldersFromServerService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncFoldersFromServerService.swift; sourceTree = "<group>"; };
   1.207 -		43C579EC1F0A465000A8EDF0 /* FetchFoldersServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchFoldersServiceTests.swift; sourceTree = "<group>"; };
   1.208  		43C6F35A1CBE7496006A2A18 /* ConnectionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionManager.swift; sourceTree = "<group>"; };
   1.209  		43C7B9D01CEC4DDF007A612F /* MiscTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MiscTests.swift; sourceTree = "<group>"; };
   1.210  		43CE63C41DE87FB200FAC505 /* Identity+pEp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Identity+pEp.swift"; sourceTree = "<group>"; };
   1.211  		43CE63CA1DE8830100FAC505 /* CdAccount+pEp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CdAccount+pEp.swift"; sourceTree = "<group>"; };
   1.212  		43CE63D01DE8866C00FAC505 /* Message+pEp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Message+pEp.swift"; sourceTree = "<group>"; };
   1.213 -		43D2C2671F13847700C97235 /* ServiceFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceFactory.swift; sourceTree = "<group>"; };
   1.214 -		43D2C2691F1385CF00C97235 /* ServiceFactoryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceFactoryTests.swift; sourceTree = "<group>"; };
   1.215  		43D51E881DD5D902008B77A8 /* SimpleOperationsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleOperationsTest.swift; sourceTree = "<group>"; };
   1.216  		43D755EA1F25D234006F933A /* MatchUidToMsnOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MatchUidToMsnOperation.swift; sourceTree = "<group>"; };
   1.217  		43D755F51F262B37006F933A /* PantomimeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PantomimeError.swift; sourceTree = "<group>"; };
   1.218 -		43D755F71F262FA0006F933A /* SyncFlagsToServerService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncFlagsToServerService.swift; sourceTree = "<group>"; };
   1.219  		43D755FB1F26382B006F933A /* EmailConnectInfo+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EmailConnectInfo+Extension.swift"; sourceTree = "<group>"; };
   1.220  		43DA52671CEF1B4F0023D540 /* NewAccountSetupUITest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewAccountSetupUITest.swift; sourceTree = "<group>"; };
   1.221 -		43DB81321E2A56BE00A20902 /* AppendDraftMailsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppendDraftMailsOperation.swift; sourceTree = "<group>"; };
   1.222 +		43DB81321E2A56BE00A20902 /* AppendMailsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppendMailsOperation.swift; sourceTree = "<group>"; };
   1.223  		43DFB0321E36083D00175C9C /* MessageHeapBufferOverflow.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MessageHeapBufferOverflow.txt; sourceTree = "<group>"; };
   1.224  		43E0CA291F4AB81600D9BB7E /* Attachment+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Attachment+Extension.swift"; sourceTree = "<group>"; };
   1.225  		43E1619020D7B2D6003F1514 /* UpdateThreadListDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateThreadListDelegate.swift; sourceTree = "<group>"; };
   1.226 @@ -971,10 +943,6 @@
   1.227  		43FAA0D11EC9972B005BFC4B /* Tuple.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tuple.swift; sourceTree = "<group>"; };
   1.228  		43FAA0D31EC9CBC0005BFC4B /* DecryptionTestsInternal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecryptionTestsInternal.swift; sourceTree = "<group>"; };
   1.229  		43FC02111F4D913D00273304 /* NSHTML_2017-08-09 15_40_53 +0000.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "NSHTML_2017-08-09 15_40_53 +0000.html"; sourceTree = "<group>"; };
   1.230 -		43FC24201F04F3F400C32110 /* ImapSmtpSyncService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImapSmtpSyncService.swift; sourceTree = "<group>"; };
   1.231 -		43FC24221F04F43000C32110 /* ImapSmtpConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImapSmtpConnection.swift; sourceTree = "<group>"; };
   1.232 -		43FC24241F04FC6E00C32110 /* MessageSyncServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageSyncServiceTests.swift; sourceTree = "<group>"; };
   1.233 -		43FC24261F04FDDF00C32110 /* SmtpSendServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SmtpSendServiceTests.swift; sourceTree = "<group>"; };
   1.234  		43FE802D209992B800E97AB3 /* QualifyServerIsLocalService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QualifyServerIsLocalService.swift; sourceTree = "<group>"; };
   1.235  		43FE802F209995AD00E97AB3 /* QualifyServerIsLocalServiceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QualifyServerIsLocalServiceTest.swift; sourceTree = "<group>"; };
   1.236  		43FE80312099F62400E97AB3 /* SubjectComposeTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SubjectComposeTextView.swift; path = ../Util/SubjectComposeTextView.swift; sourceTree = "<group>"; };
   1.237 @@ -1172,6 +1140,14 @@
   1.238  			path = IMAP;
   1.239  			sourceTree = "<group>";
   1.240  		};
   1.241 +		1569AEA320E14DC2002102A0 /* Features */ = {
   1.242 +			isa = PBXGroup;
   1.243 +			children = (
   1.244 +				1569AEA420E14DC2002102A0 /* ReUploadTest.swift */,
   1.245 +			);
   1.246 +			path = Features;
   1.247 +			sourceTree = "<group>";
   1.248 +		};
   1.249  		156BEDED1FCC563B006C1492 /* AttachmentCell */ = {
   1.250  			isa = PBXGroup;
   1.251  			children = (
   1.252 @@ -1338,9 +1314,7 @@
   1.253  				43122B3D1DF5BB6600610253 /* MySelfOperation.swift */,
   1.254  				43A26FBE1E041BF200AF0B84 /* ImapSyncOperation.swift */,
   1.255  				43BBB5E61E267A3800104070 /* EncryptAndSendOperation.swift */,
   1.256 -				4301B3151E28D259007D626C /* AppendMailsOperationBase.swift */,
   1.257 -				43DB81321E2A56BE00A20902 /* AppendDraftMailsOperation.swift */,
   1.258 -				153D08271F56BBCB00377110 /* AppendSendMailsOperation.swift */,
   1.259 +				43DB81321E2A56BE00A20902 /* AppendMailsOperation.swift */,
   1.260  				4333A2A41E5C9B6E0025D8A5 /* FixAttachmentsOperation.swift */,
   1.261  				4326D3FE1EFBC8DB0016AB0D /* FolderInfoOperation.swift */,
   1.262  				431E2B061F02550C000035FA /* CheckOutgoingMessagesOperation.swift */,
   1.263 @@ -1449,18 +1423,6 @@
   1.264  			children = (
   1.265  				432DA7431EE01E3300B30BAA /* MessageSyncServiceProtocol.swift */,
   1.266  				432DA7451EE027EB00B30BAA /* MessageSyncService.swift */,
   1.267 -				43FC24201F04F3F400C32110 /* ImapSmtpSyncService.swift */,
   1.268 -				43FC24221F04F43000C32110 /* ImapSmtpConnection.swift */,
   1.269 -				43C579E81F0A1FEC00A8EDF0 /* AtomicImapService.swift */,
   1.270 -				435DF9081F2A26F2003254F7 /* BackgroundOperationImapService.swift */,
   1.271 -				431E2B081F0281DF000035FA /* SmtpSendService.swift */,
   1.272 -				43C579EA1F0A382400A8EDF0 /* SyncFoldersFromServerService.swift */,
   1.273 -				43200D9C1F0CBC4000FFDE56 /* FetchMessagesService.swift */,
   1.274 -				432A3E3E1F0E6B3700834749 /* SyncExistingMessagesService.swift */,
   1.275 -				43D755F71F262FA0006F933A /* SyncFlagsToServerService.swift */,
   1.276 -				430EA5F91F0FAD7700F816D4 /* ServiceChainExecutor.swift */,
   1.277 -				43D2C2671F13847700C97235 /* ServiceFactory.swift */,
   1.278 -				432677151F17796D00F01F5A /* ImapIdleService.swift */,
   1.279  			);
   1.280  			path = MessageSyncService;
   1.281  			sourceTree = "<group>";
   1.282 @@ -1653,6 +1615,7 @@
   1.283  				43FE802F209995AD00E97AB3 /* QualifyServerIsLocalServiceTest.swift */,
   1.284  				151F71EB202A06750057C74D /* TestUtils */,
   1.285  				43B2C3151D2280ED00A08557 /* Resources */,
   1.286 +				1569AEA320E14DC2002102A0 /* Features */,
   1.287  				15BBBC691FD0522E00B9DCC8 /* Error */,
   1.288  				15B483DF1F290B14000FB2CF /* Background */,
   1.289  				4918EBF91E783C70006207FC /* Models */,
   1.290 @@ -1678,14 +1641,7 @@
   1.291  				43C3B15F2003851100ED48A4 /* DecryptImportedMessagesTests.swift */,
   1.292  				431E58FB1ED5926B00EFA77F /* AccountVerificationServiceTests.swift */,
   1.293  				436795F71EE98B9A00B03E23 /* MessageReevalutionTests.swift */,
   1.294 -				43FC24241F04FC6E00C32110 /* MessageSyncServiceTests.swift */,
   1.295 -				43FC24261F04FDDF00C32110 /* SmtpSendServiceTests.swift */,
   1.296 -				43C579EC1F0A465000A8EDF0 /* FetchFoldersServiceTests.swift */,
   1.297 -				43200D9E1F0CD81300FFDE56 /* FetchMessagesServiceTests.swift */,
   1.298 -				432A3E431F0E793A00834749 /* SyncExistingMessagesServiceTests.swift */,
   1.299 -				430EA5FB1F0FB92A00F816D4 /* ServiceChainExecutorTests.swift */,
   1.300  				15B483DA1F28E2FC000FB2CF /* SpecialUseMailboxesTest.swift */,
   1.301 -				43D2C2691F1385CF00C97235 /* ServiceFactoryTests.swift */,
   1.302  				B70DE6871F2773BF00C0A50A /* EmailValidatiorTest.swift */,
   1.303  				43F7F0791F6AD44600BDF151 /* HandshakeTests.swift */,
   1.304  				431F987E1F6FD3E300A1E4D2 /* HandshakePartnerTableViewCellViewModelTests.swift */,
   1.305 @@ -1824,6 +1780,10 @@
   1.306  		43B2C3151D2280ED00A08557 /* Resources */ = {
   1.307  			isa = PBXGroup;
   1.308  			children = (
   1.309 +				15EBE44020E5353D00268859 /* unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_pub.asc */,
   1.310 +				15EBE44120E5353D00268859 /* unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_sec.asc */,
   1.311 +				15EBE43C20E5296900268859 /* unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_pub.asc */,
   1.312 +				15EBE43920E5286500268859 /* unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_sec.asc */,
   1.313  				4395CDEF20AAD0D6003FC5F1 /* icon_001.gif */,
   1.314  				43C3B16320038B2500ED48A4 /* IOS-884_001_iostest002@peptest.ch.pub.key */,
   1.315  				43C3B16420038B2500ED48A4 /* IOS-884_001_iostest002@peptest.ch.sec.key */,
   1.316 @@ -2496,12 +2456,16 @@
   1.317  				4348EF151E27E37400F441A9 /* Unit 1 unittest.ios.1@peptest.ch (0x9CB8DBCC) pub.asc in Resources */,
   1.318  				43C3B16620038B2500ED48A4 /* IOS-884_001_iostest002@peptest.ch.pub.key in Resources */,
   1.319  				431BB9451E49B7A9000BCBF1 /* PorpoiseGalaxy_HubbleFraile_960.jpg in Resources */,
   1.320 +				15EBE44220E5353E00268859 /* unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_pub.asc in Resources */,
   1.321 +				15EBE44320E5353E00268859 /* unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_sec.asc in Resources */,
   1.322 +				15EBE43B20E5286500268859 /* unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_sec.asc in Resources */,
   1.323  				43F7F07C1F6AD4FD00BDF151 /* HandshakeTests_mail_001.txt in Resources */,
   1.324  				43C3B16820038B2500ED48A4 /* IOS-884_001_test010@peptest.ch.pub.key in Resources */,
   1.325  				436795FB1EE98E9900B03E23 /* CommunicationTypeTests_test002@peptest.ch_sec.asc in Resources */,
   1.326  				430C80D61D0EAB6E00CD4582 /* pEpTrustWords.bundle in Resources */,
   1.327  				436796001EE98F6E00B03E23 /* CommunicationTypeTests_test001@peptest.ch.asc in Resources */,
   1.328  				436796021EE9909100B03E23 /* CommunicationTypeTests_Message_test001_to_test002.txt in Resources */,
   1.329 +				15EBE43E20E5296900268859 /* unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_pub.asc in Resources */,
   1.330  				4395CDF020AAD0D7003FC5F1 /* icon_001.gif in Resources */,
   1.331  				438D25401D4B9EDB00BFF7AA /* PGPMimeMail.txt in Resources */,
   1.332  				43C3B1622003896800ED48A4 /* IOS-884_001_Mail_from_P4A.txt in Resources */,
   1.333 @@ -2617,7 +2581,6 @@
   1.334  				43EE99481E438E320060EACE /* FlagImages.swift in Sources */,
   1.335  				220DCE341E0AB5CC002FE716 /* MailinglistCell.swift in Sources */,
   1.336  				436239941EAE08F400BD2EB9 /* String+Extensions.swift in Sources */,
   1.337 -				43D2C2681F13847700C97235 /* ServiceFactory.swift in Sources */,
   1.338  				43AA825D1E9BD60600ABD5A8 /* AttachmentsViewDelegate.swift in Sources */,
   1.339  				220DCE371E0AB5CC002FE716 /* MessageSubjectCell.swift in Sources */,
   1.340  				43AE48E71EEFC93900B92BB6 /* DebugMergePolicy.swift in Sources */,
   1.341 @@ -2631,7 +2594,6 @@
   1.342  				5D039AA21D0EEA3700AD59EC /* DecryptMessagesOperation.swift in Sources */,
   1.343  				439AAC561F21D0CC0018A29E /* CdFolder+Pantomime.swift in Sources */,
   1.344  				4351C2D51F4441190053381F /* latex.c in Sources */,
   1.345 -				153D08281F56BBCB00377110 /* AppendSendMailsOperation.swift in Sources */,
   1.346  				434BF39120C6B02100FCBCCA /* Message+ThreadAware.swift in Sources */,
   1.347  				439932291FEA7E7A00E92C35 /* String+Email.swift in Sources */,
   1.348  				43498CDB200CF20F006DC947 /* LoginViewModelLoginErrorDelegate.swift in Sources */,
   1.349 @@ -2658,9 +2620,7 @@
   1.350  				5D039A9C1D0EE7F900AD59EC /* PEPUtil.swift in Sources */,
   1.351  				43257C841F50629700DDC7F0 /* HtmlToAttributedTextSaxParser.swift in Sources */,
   1.352  				1541D7F31FC8292D00D52A5D /* URL+MIME.swift in Sources */,
   1.353 -				43D755F81F262FA0006F933A /* SyncFlagsToServerService.swift in Sources */,
   1.354  				43088F742046C08000BB5149 /* ComposeMessageBodyTextView.swift in Sources */,
   1.355 -				43C579E91F0A1FEC00A8EDF0 /* AtomicImapService.swift in Sources */,
   1.356  				222B35691DF96AFE007A1F82 /* ComposeTextView.swift in Sources */,
   1.357  				4351C2D61F4441190053381F /* man.c in Sources */,
   1.358  				43F6DFD71DEEC752006B526F /* FetchMessagesOperation.swift in Sources */,
   1.359 @@ -2691,7 +2651,6 @@
   1.360  				43040A531E9776220083DED8 /* AttachmentSummaryView.swift in Sources */,
   1.361  				431D60DB1E93BB2D001266D7 /* AttachmentsView.swift in Sources */,
   1.362  				43ED53791CC77F95006AB156 /* UserInfoTableViewController.swift in Sources */,
   1.363 -				43200D9D1F0CBC4000FFDE56 /* FetchMessagesService.swift in Sources */,
   1.364  				43C322101EA91764005073FB /* UIImage+Extension.swift in Sources */,
   1.365  				15865A8920319ADC00F7A4B5 /* Folder+pEp.swift in Sources */,
   1.366  				1547509A1FE7C0B4000D8004 /* FetchNumberOfNewMailsOperation.swift in Sources */,
   1.367 @@ -2700,7 +2659,7 @@
   1.368  				43122B1B1DF5B48B00610253 /* SmtpService.swift in Sources */,
   1.369  				43CE63C51DE87FB200FAC505 /* Identity+pEp.swift in Sources */,
   1.370  				220DCE2E1E0AB544002FE716 /* MessageCell.swift in Sources */,
   1.371 -				43DB81331E2A56BE00A20902 /* AppendDraftMailsOperation.swift in Sources */,
   1.372 +				43DB81331E2A56BE00A20902 /* AppendMailsOperation.swift in Sources */,
   1.373  				8B69E3991E30F80E0022959E /* Appearance.swift in Sources */,
   1.374  				43F848491EAA09AE00DBE460 /* Weak.swift in Sources */,
   1.375  				B716056020D3ECC900A733D6 /* MoveToFolderTableViewController.swift in Sources */,
   1.376 @@ -2708,7 +2667,6 @@
   1.377  				43D755FC1F26382B006F933A /* EmailConnectInfo+Extension.swift in Sources */,
   1.378  				431394C11E4B11CF00D92F33 /* AppSettings.swift in Sources */,
   1.379  				4381D0FF1E51A124002743C3 /* NetworkServiceWorker.swift in Sources */,
   1.380 -				432677161F17796D00F01F5A /* ImapIdleService.swift in Sources */,
   1.381  				B72528331EE948B800815118 /* AccountsSettingsViewModel.swift in Sources */,
   1.382  				15EE5A75203B15670041F076 /* SettingBaseViewController.swift in Sources */,
   1.383  				43E657E71F3CAB310014CBEC /* HtmlToMarkdownSaxParser.swift in Sources */,
   1.384 @@ -2766,7 +2724,6 @@
   1.385  				00AEB2F620DBA7DA00DA185A /* NeedsRefreshDelegate.swift in Sources */,
   1.386  				8B81BB901E03F73100D38C82 /* SegueHandlerType.swift in Sources */,
   1.387  				B741D5031F26234A00DFD93A /* EmailAddressValidation.swift in Sources */,
   1.388 -				4301B3161E28D259007D626C /* AppendMailsOperationBase.swift in Sources */,
   1.389  				15A75CE1202B57BF00C0DA28 /* Folder+VirtualMailbox.swift in Sources */,
   1.390  				B72528351EE953BC00815118 /* AccountsSettingsCellViewModel.swift in Sources */,
   1.391  				43122B1A1DF5B48B00610253 /* ImapService.swift in Sources */,
   1.392 @@ -2774,7 +2731,6 @@
   1.393  				43A6E04B1E5726C8005BEE69 /* ReevaluateMessageRatingOperation.swift in Sources */,
   1.394  				15B220521FC2DAE200CA52BA /* InfoPlist+pEpStrings.swift in Sources */,
   1.395  				15C5F2461F823752007DE086 /* SortedSet.swift in Sources */,
   1.396 -				43FC24211F04F3F400C32110 /* ImapSmtpSyncService.swift in Sources */,
   1.397  				431E2B071F02550C000035FA /* CheckOutgoingMessagesOperation.swift in Sources */,
   1.398  				43ED536F1CC77F95006AB156 /* EmailListViewCell.swift in Sources */,
   1.399  				432142641E8FD66900FBE987 /* FetchNumberOfNewMailsService.swift in Sources */,
   1.400 @@ -2819,7 +2775,6 @@
   1.401  				43E88BE01EFA667600E0B224 /* AsyncStateMachine.swift in Sources */,
   1.402  				00EB89AD20E3D3C200CDFA0D /* ThreadedEmailViewModel+MoveToFolderDelegate.swift in Sources */,
   1.403  				431B04801DE5774800E40CD3 /* CdMessage+Pantomime.swift in Sources */,
   1.404 -				435DF9091F2A26F2003254F7 /* BackgroundOperationImapService.swift in Sources */,
   1.405  				431144B71CC11D6A0007639D /* BaseOperation.swift in Sources */,
   1.406  				437E492A20E50B9500BF959C /* ThreadAwareFolderWithTopFactory.swift in Sources */,
   1.407  				432198E81DF6B51B00318A74 /* LoginImapOperation.swift in Sources */,
   1.408 @@ -2856,7 +2811,6 @@
   1.409  				1500199D1F2B2C73003E670A /* Notification+CWServiceClientNotificationParsing.swift in Sources */,
   1.410  				496C0EE720BC2A880009B5B9 /* EmailDisplayDelegate.swift in Sources */,
   1.411  				1579397B1F4E00AF00A2A6CF /* UINavigationController+Extensions.swift in Sources */,
   1.412 -				431E2B091F0281DF000035FA /* SmtpSendService.swift in Sources */,
   1.413  				43306EC21FE129840045DD00 /* OAuth2Type.swift in Sources */,
   1.414  				43CE63CB1DE8830100FAC505 /* CdAccount+pEp.swift in Sources */,
   1.415  				438052871FE3E1B100ACF729 /* OAuth2AuthorizationFactoryProtocol.swift in Sources */,
   1.416 @@ -2865,7 +2819,6 @@
   1.417  				153FC45D202A263D0053CCF1 /* FolderType+IMAP.swift in Sources */,
   1.418  				15BA536A20A08B100090F126 /* Account+Extentions.swift in Sources */,
   1.419  				15255B031F825CD100A2CFC9 /* IdentityImageTool.swift in Sources */,
   1.420 -				432A3E3F1F0E6B3700834749 /* SyncExistingMessagesService.swift in Sources */,
   1.421  				43BBB5E71E267A3800104070 /* EncryptAndSendOperation.swift in Sources */,
   1.422  				434BF39520C6B72000FCBCCA /* ThreadAwareMessageProtocol.swift in Sources */,
   1.423  				43AA82531E9B925C00ABD5A8 /* UIView+Util.swift in Sources */,
   1.424 @@ -2874,7 +2827,6 @@
   1.425  				4315E4BF2011FD6900F68763 /* AuthMethod.swift in Sources */,
   1.426  				434F40961EB0DB5E002FBF0D /* HandshakePartnerTableViewCellViewModel.swift in Sources */,
   1.427  				43E9BC641DB518A700AD2352 /* CdFolder+Extension.swift in Sources */,
   1.428 -				430EA5FA1F0FAD7700F816D4 /* ServiceChainExecutor.swift in Sources */,
   1.429  				439F127020C7BF2E002FF13E /* ThreadAwareFolderFactory.swift in Sources */,
   1.430  				4351C2CC1F4441190053381F /* cmark_ctype.c in Sources */,
   1.431  				432AC3061ECB0C44007DC418 /* CWIMAPMessage+pEp.swift in Sources */,
   1.432 @@ -2894,7 +2846,6 @@
   1.433  				43F8D80420C538740038ABD5 /* ThreadUnAwareFolderFactory.swift in Sources */,
   1.434  				43106A192045716000693144 /* OAuth2ConfigurationProtocol+Extension.swift in Sources */,
   1.435  				432142661E8FD6A400FBE987 /* ServiceUtil.swift in Sources */,
   1.436 -				43FC24231F04F43000C32110 /* ImapSmtpConnection.swift in Sources */,
   1.437  				43C322051EA89EED005073FB /* HandshakePartnerTableViewCell.swift in Sources */,
   1.438  				43E0CA2A1F4AB81600D9BB7E /* Attachment+Extension.swift in Sources */,
   1.439  				15BA536C20A08D270090F126 /* UnifiedInbox.swift in Sources */,
   1.440 @@ -2910,7 +2861,6 @@
   1.441  				43B10C801EC2EE7F003E849F /* CppDummy.cpp in Sources */,
   1.442  				15B220501FBF5D6E00CA52BA /* InfoPlist.swift in Sources */,
   1.443  				A1014DA71D1173CD00C472A8 /* UIHelper.swift in Sources */,
   1.444 -				43C579EB1F0A382400A8EDF0 /* SyncFoldersFromServerService.swift in Sources */,
   1.445  				4388A0E42008F8F4008CB98D /* OAuth2AccessTokenProtocol.swift in Sources */,
   1.446  				43A6E0581E57400E005BEE69 /* RatingReEvaluator.swift in Sources */,
   1.447  				49C34AF620E4F649009D11CC /* CellDetailTransition.swift in Sources */,
   1.448 @@ -2989,7 +2939,6 @@
   1.449  				151F71FA202A06760057C74D /* DecryptionAttemptCounterDelegate.swift in Sources */,
   1.450  				431C2B171F387C4100D87FFD /* LoginViewModelTests.swift in Sources */,
   1.451  				15F835281F3B497D00FCE887 /* FetchMessagesOperationTest.swift in Sources */,
   1.452 -				430EA5FC1F0FB92A00F816D4 /* ServiceChainExecutorTests.swift in Sources */,
   1.453  				436795F81EE98B9A00B03E23 /* MessageReevalutionTests.swift in Sources */,
   1.454  				43EE994E1E4392530060EACE /* FlagImageTests.swift in Sources */,
   1.455  				153CA6E41FB60D99003C9629 /* DercyptMessagesOperationTest.swift in Sources */,
   1.456 @@ -3006,33 +2955,28 @@
   1.457  				43C3B1602003851100ED48A4 /* DecryptImportedMessagesTests.swift in Sources */,
   1.458  				1500199F1F2BA2EF003E670A /* SyncFlagsToServerOperationTest.swift in Sources */,
   1.459  				432F7D611D2102F10094F097 /* PEPSessionTest.swift in Sources */,
   1.460 -				43C579ED1F0A465000A8EDF0 /* FetchFoldersServiceTests.swift in Sources */,
   1.461  				151F7205202A070E0057C74D /* PantomimeFolderAttribute+ExtensionsTest.swift in Sources */,
   1.462  				43504D4420E0DD0E00D11E52 /* EmailListViewModel+ThreadingTests.swift in Sources */,
   1.463  				43E74C741E38DE2200A2F7A6 /* ImapFlagsTests.swift in Sources */,
   1.464  				15B483E11F290B14000FB2CF /* LoginImapOperationTest.swift in Sources */,
   1.465  				430BA229203438A3003B041C /* CdMessagePEPMessageTests.swift in Sources */,
   1.466  				431F987F1F6FD3E300A1E4D2 /* HandshakePartnerTableViewCellViewModelTests.swift in Sources */,
   1.467 -				432A3E441F0E793A00834749 /* SyncExistingMessagesServiceTests.swift in Sources */,
   1.468  				1541D7F51FC82A4900D52A5D /* URL+MIME.swift in Sources */,
   1.469  				438281831E891B7E00087343 /* DateTests.swift in Sources */,
   1.470  				15B483DB1F28E2FC000FB2CF /* SpecialUseMailboxesTest.swift in Sources */,
   1.471  				151F71FB202A06760057C74D /* MockBackgrounder.swift in Sources */,
   1.472  				43D51E891DD5D902008B77A8 /* SimpleOperationsTest.swift in Sources */,
   1.473  				151F71FE202A06760057C74D /* CdMessage+TestUtils.swift in Sources */,
   1.474 -				43FC24251F04FC6E00C32110 /* MessageSyncServiceTests.swift in Sources */,
   1.475  				151F71F9202A06760057C74D /* NetworkServiceObserver.swift in Sources */,
   1.476  				153FC45F202A26B30053CCF1 /* FolderType+IMAPTest.swift in Sources */,
   1.477  				43F7F07A1F6AD44600BDF151 /* HandshakeTests.swift in Sources */,
   1.478 +				1569AEA520E14DC3002102A0 /* ReUploadTest.swift in Sources */,
   1.479  				B70DE6881F2773BF00C0A50A /* EmailValidatiorTest.swift in Sources */,
   1.480  				430C80E01D0EADC200CD4582 /* PepAdapterTests.swift in Sources */,
   1.481  				5DEBAA481DE59C3B00FAE12C /* NetworkServiceTests.swift in Sources */,
   1.482  				151F7200202A06760057C74D /* TestUtil.swift in Sources */,
   1.483  				43FE8030209995AD00E97AB3 /* QualifyServerIsLocalServiceTest.swift in Sources */,
   1.484  				1508AEAA1F862C85001D5230 /* SortedSetTest.swift in Sources */,
   1.485 -				43D2C26A1F1385CF00C97235 /* ServiceFactoryTests.swift in Sources */,
   1.486 -				43200D9F1F0CD81300FFDE56 /* FetchMessagesServiceTests.swift in Sources */,
   1.487 -				43FC24271F04FDDF00C32110 /* SmtpSendServiceTests.swift in Sources */,
   1.488  				43257C861F50659200DDC7F0 /* StringHTMLExtensionTests.swift in Sources */,
   1.489  			);
   1.490  			runOnlyForDeploymentPostprocessing = 0;
     2.1 --- a/pEpForiOS/AppDelegate.swift	Wed Jul 04 11:24:51 2018 +0200
     2.2 +++ b/pEpForiOS/AppDelegate.swift	Wed Jul 04 11:27:58 2018 +0200
     2.3 @@ -211,7 +211,7 @@
     2.4          // Needs to be done once to inform all affected services about the current settings
     2.5          let _ = AppSettings()
     2.6  
     2.7 -        let theMessageSyncService = MessageSyncService(mySelfer: self)
     2.8 +        let theMessageSyncService = MessageSyncService()
     2.9          messageSyncService = theMessageSyncService
    2.10          let theAppConfig = AppConfig(mySelfer: self,
    2.11                                       messageSyncService: theMessageSyncService,
    2.12 @@ -247,6 +247,11 @@
    2.13  
    2.14      // MARK: - UIApplicationDelegate
    2.15  
    2.16 +    func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
    2.17 +        Log.shared.warn(component: "UIApplicationDelegate",
    2.18 +                        content: "applicationDidReceiveMemoryWarning")
    2.19 +    }
    2.20 +
    2.21      func application(
    2.22          _ application: UIApplication, didFinishLaunchingWithOptions
    2.23          launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
     3.1 --- a/pEpForiOS/Background/AppendDraftMailsOperation.swift	Wed Jul 04 11:24:51 2018 +0200
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,39 +0,0 @@
     3.4 -//
     3.5 -//  AppendDraftMailsOperation.swift
     3.6 -//  pEpForiOS
     3.7 -//
     3.8 -//  Created by Dirk Zimmermann on 14/01/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 class AppendDraftMailsOperation: AppendMailsOperationBase {
    3.18 -    public init(
    3.19 -        parentName: String = #function, imapSyncData: ImapSyncData,
    3.20 -        errorContainer: ServiceErrorProtocol = ErrorContainer()) {
    3.21 -        let appendFolder = FolderType.drafts
    3.22 -        super.init(parentName: parentName, appendFolderType: appendFolder,
    3.23 -                   imapSyncData: imapSyncData, errorContainer: errorContainer,
    3.24 -                   encryptMode: .encryptToMySelf)
    3.25 -    }
    3.26 -
    3.27 -    override func retrieveNextMessage() -> (PEPMessageDict, PEPIdentity, NSManagedObjectID)? {
    3.28 -        var result: (PEPMessageDict, PEPIdentity, NSManagedObjectID)? = nil
    3.29 -        privateMOC.performAndWait {
    3.30 -            let p = NSPredicate(
    3.31 -                format: "uid = 0 AND parent.folderTypeRawValue = %d AND sendStatusRawValue = %d AND parent.account = %@",
    3.32 -                targetFolderType.rawValue, SendStatus.none.rawValue,
    3.33 -                imapSyncData.connectInfo.accountObjectID)
    3.34 -            let msg = CdMessage.first(predicate: p, in: self.privateMOC)
    3.35 -            if let m = msg, let cdIdent = m.parent?.account?.identity {
    3.36 -                result = (m.pEpMessageDict(), cdIdent.pEpIdentity(), m.objectID)
    3.37 -            }
    3.38 -        }
    3.39 -        
    3.40 -        return result
    3.41 -    }
    3.42 -}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/pEpForiOS/Background/AppendMailsOperation.swift	Wed Jul 04 11:27:58 2018 +0200
     4.3 @@ -0,0 +1,182 @@
     4.4 +//
     4.5 +//  AppendDraftMailsOperation.swift
     4.6 +//  pEpForiOS
     4.7 +//
     4.8 +//  Created by Dirk Zimmermann on 14/01/2017.
     4.9 +//  Copyright © 2017 p≡p Security S.A. All rights reserved.
    4.10 +//
    4.11 +
    4.12 +import UIKit
    4.13 +import CoreData
    4.14 +
    4.15 +import MessageModel
    4.16 +
    4.17 +/// Operation for storing mails in any type of IMAP folder.
    4.18 +public class AppendMailsOperation: ImapSyncOperation {
    4.19 +    enum EncryptMode {
    4.20 +        case forSelf
    4.21 +        case unencryptedForTrustedServer
    4.22 +    }
    4.23 +    private var encryptMode: EncryptMode {
    4.24 +        return imapSyncData.connectInfo.trusted ? .unencryptedForTrustedServer : .forSelf
    4.25 +    }
    4.26 +    private var syncDelegate: AppendMailsSyncDelegate?
    4.27 +
    4.28 +    /** The object ID of the last handled message, so we can modify/delete it on success */
    4.29 +    var lastHandledMessageObjectID: NSManagedObjectID?
    4.30 +
    4.31 +    private let folder: Folder
    4.32 +
    4.33 +    init(parentName: String = #function, folder: Folder, imapSyncData: ImapSyncData,
    4.34 +         errorContainer: ServiceErrorProtocol = ErrorContainer()) {
    4.35 +        self.folder = folder
    4.36 +        super.init(parentName: parentName, errorContainer: errorContainer,
    4.37 +                   imapSyncData: imapSyncData)
    4.38 +    }
    4.39 +
    4.40 +    override public func main() {
    4.41 +        if !checkImapSync() {
    4.42 +            markAsFinished()
    4.43 +            return
    4.44 +        }
    4.45 +        syncDelegate = AppendMailsSyncDelegate(errorHandler: self)
    4.46 +        imapSyncData.sync?.delegate = syncDelegate
    4.47 +
    4.48 +        handleNextMessage()
    4.49 +    }
    4.50 +
    4.51 +    func retrieveNextMessage() -> (PEPMessageDict, PEPIdentity, NSManagedObjectID)? {
    4.52 +        var result: (PEPMessageDict, PEPIdentity, NSManagedObjectID)? = nil
    4.53 +        privateMOC.performAndWait { [weak self] in
    4.54 +            guard let me = self else {
    4.55 +                Log.shared.errorAndCrash(component: #function, errorString: "Lost myself")
    4.56 +                return
    4.57 +            }
    4.58 +            guard let account = privateMOC.object(with: imapSyncData.connectInfo.accountObjectID) as? CdAccount,
    4.59 +                let address = account.identity?.address
    4.60 +                else {
    4.61 +                    Log.shared.errorAndCrash(component: #function, errorString: "Missing data")
    4.62 +                    result = nil
    4.63 +                    return
    4.64 +            }
    4.65 +            let p = CdMessage.PredicateFactory.needImapAppend(inFolderNamed: me.folder.name,
    4.66 +                                                              inAccountWithAddress: address)
    4.67 +            let msg = CdMessage.first(predicate: p, in: me.privateMOC)
    4.68 +            if let m = msg, let cdIdent = m.parent?.account?.identity {
    4.69 +                result = (m.pEpMessageDict(), cdIdent.pEpIdentity(), m.objectID)
    4.70 +            }
    4.71 +        }
    4.72 +        return result
    4.73 +    }
    4.74 +
    4.75 +    func markLastMessageAsFinished() {
    4.76 +        if let msgID = lastHandledMessageObjectID {
    4.77 +            privateMOC.performAndWait { [weak self] in
    4.78 +                guard let me = self else {
    4.79 +                    Log.shared.errorAndCrash(component: #function, errorString: "I got lost")
    4.80 +                    return
    4.81 +                }
    4.82 +                if let obj = me.privateMOC.object(with: msgID) as? CdMessage {
    4.83 +                    me.privateMOC.delete(obj)
    4.84 +                    me.privateMOC.saveAndLogErrors()
    4.85 +                } else {
    4.86 +                    Log.shared.errorAndCrash(component: #function,
    4.87 +                                             errorString: "Message dissapeared")
    4.88 +                    me.handleError(BackgroundError.GeneralError.invalidParameter(info: #function),
    4.89 +                                   message: "Cannot find message just stored.")
    4.90 +                    return
    4.91 +                }
    4.92 +            }
    4.93 +        }
    4.94 +    }
    4.95 +
    4.96 +    private func appendMessage(pEpMessageDict msg: PEPMessageDict) {
    4.97 +        let pantMail = PEPUtil.pantomime(pEpMessageDict: msg)
    4.98 +        let cwFolder = CWIMAPFolder(name: folder.name)
    4.99 +        if let sync = imapSyncData.sync {
   4.100 +            cwFolder.setStore(sync.imapStore)
   4.101 +        }
   4.102 +        guard let rawData = pantMail.dataValue() else {
   4.103 +            Log.shared.errorAndCrash(component: #function, errorString: "No data")
   4.104 +            markAsFinished()
   4.105 +            return
   4.106 +        }
   4.107 +        let flags = self.folder.folderType.defaultAppendImapFlags()
   4.108 +        cwFolder.appendMessage(fromRawSource: rawData,
   4.109 +                               flags: flags?.pantomimeFlags(),
   4.110 +                               internalDate: nil)
   4.111 +    }
   4.112 +
   4.113 +    private func encrypt(session: PEPSession, pEpMessageDict: PEPMessageDict, forSelf: PEPIdentity? = nil)
   4.114 +        throws -> (PEP_STATUS, NSDictionary?) {
   4.115 +            return try session.encrypt(pEpMessageDict: pEpMessageDict, forSelf: forSelf)
   4.116 +    }
   4.117 +
   4.118 +    fileprivate func handleNextMessage() {
   4.119 +        markLastMessageAsFinished()
   4.120 +        guard !isCancelled else {
   4.121 +            waitForBackgroundTasksToFinish()
   4.122 +            return
   4.123 +        }
   4.124 +        guard let (msg, ident, objID) = retrieveNextMessage() else {
   4.125 +            markAsFinished()
   4.126 +            return
   4.127 +        }
   4.128 +        lastHandledMessageObjectID = objID
   4.129 +
   4.130 +        if encryptMode == .unencryptedForTrustedServer {
   4.131 +            // Always append unencrypted for trusted server.
   4.132 +            appendMessage(pEpMessageDict: msg)
   4.133 +        } else if encryptMode == .forSelf {
   4.134 +            do {
   4.135 +                let session = PEPSession()
   4.136 +                let (_, encMsg) = try encrypt(session: session, pEpMessageDict: msg, forSelf: ident)
   4.137 +                guard let msgDict = encMsg as? PEPMessageDict else {
   4.138 +                    Log.shared.errorAndCrash(component: #function, errorString: "Error casting")
   4.139 +                    handleError(BackgroundError.GeneralError.illegalState(info: "Eror casting"),
   4.140 +                                message: "Error casting")
   4.141 +                    return
   4.142 +                }
   4.143 +                // ...  and append.
   4.144 +                appendMessage(pEpMessageDict: msgDict)
   4.145 +            } catch let err as NSError {
   4.146 +                handleError(err, message: "Cannot encrypt message")
   4.147 +            }
   4.148 +        }
   4.149 +    }
   4.150 +
   4.151 +    override func markAsFinished() {
   4.152 +        syncDelegate = nil
   4.153 +        super.markAsFinished()
   4.154 +    }
   4.155 +
   4.156 +    static func foldersContainingMarkedForAppend(connectInfo: EmailConnectInfo) -> [Folder] {
   4.157 +        var result = [Folder]()
   4.158 +        let privateMOC = Record.Context.background
   4.159 +        privateMOC.performAndWait {
   4.160 +            guard
   4.161 +                let cdAccount =
   4.162 +                privateMOC.object(with: connectInfo.accountObjectID) as? CdAccount
   4.163 +                else {
   4.164 +                    Log.shared.errorAndCrash(component: #function, errorString: "No account")
   4.165 +                    return
   4.166 +            }
   4.167 +            let appendMessages = Message.allMessagesMarkedForAppend(inAccount: cdAccount.account())
   4.168 +            let foldersContainingMessagesForAppend = appendMessages.map { $0.parent }
   4.169 +            result = Array(Set(foldersContainingMessagesForAppend))
   4.170 +        }
   4.171 +
   4.172 +        return result
   4.173 +    }
   4.174 +}
   4.175 +
   4.176 +class AppendMailsSyncDelegate: DefaultImapSyncDelegate {
   4.177 +    public override func folderAppendCompleted(_ sync: ImapSync, notification: Notification?) {
   4.178 +        (errorHandler as? AppendMailsOperation)?.handleNextMessage()
   4.179 +    }
   4.180 +
   4.181 +    public override func folderAppendFailed(_ sync: ImapSync, notification: Notification?) {
   4.182 +        (errorHandler as? AppendMailsOperation)?.addIMAPError(ImapSyncError.folderAppendFailed)
   4.183 +        (errorHandler as? AppendMailsOperation)?.markAsFinished()
   4.184 +    }
   4.185 +}
     5.1 --- a/pEpForiOS/Background/AppendMailsOperationBase.swift	Wed Jul 04 11:24:51 2018 +0200
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,222 +0,0 @@
     5.4 -//
     5.5 -//  AppendMailsOperationBase.swift
     5.6 -//  pEpForiOS
     5.7 -//
     5.8 -//  Created by Dirk Zimmermann on 13/01/2017.
     5.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
    5.10 -//
    5.11 -
    5.12 -import CoreData
    5.13 -import MessageModel
    5.14 -
    5.15 -/**
    5.16 - Base class for storing mails in any type of folder.
    5.17 -
    5.18 - Stores messges retreived by `retrieveNextMessage` to folder of type `targetFolderType`.
    5.19 - Mails are encrypted whenever possible before storing it in the target folder .
    5.20 -
    5.21 - Subclasses MUST override `retrieveNextMessage`
    5.22 - For marking the message as done, you MAY overwrite `markLastMessageAsFinished`.
    5.23 - */
    5.24 -public class AppendMailsOperationBase: ImapSyncOperation {
    5.25 -    public enum EncryptMode {
    5.26 -        // Encrypt messages for myself
    5.27 -        case encryptToMySelf
    5.28 -
    5.29 -        // Encrypt messages as if they were outgoing
    5.30 -        case encryptAsOutgoing
    5.31 -
    5.32 -        public func encrypt(session: PEPSession, pEpMessageDict: PEPMessageDict,
    5.33 -                            forSelf: PEPIdentity? = nil)
    5.34 -            throws -> (PEP_STATUS, NSDictionary?) {
    5.35 -                switch self {
    5.36 -                case .encryptToMySelf:
    5.37 -                    return try session.encrypt(
    5.38 -                        pEpMessageDict: pEpMessageDict, forSelf: forSelf)
    5.39 -                case .encryptAsOutgoing:
    5.40 -                    return try session.encrypt(pEpMessageDict: pEpMessageDict)
    5.41 -                }
    5.42 -        }
    5.43 -    }
    5.44 -
    5.45 -    var syncDelegate: AppendMailsSyncDelegate?
    5.46 -
    5.47 -    /** The object ID of the last handled message, so we can modify/delete it on success */
    5.48 -    var lastHandledMessageObjectID: NSManagedObjectID?
    5.49 -
    5.50 -    private var targetFolderName: String?
    5.51 -    let targetFolderType: FolderType
    5.52 -
    5.53 -    /** On finish, the messageIDs of the messages that have been sent successfully */
    5.54 -    private(set) var successAppendedMessageIDs = [String]()
    5.55 -
    5.56 -    /**
    5.57 -     This changes the encryption that is used for the message to be appended.
    5.58 -     */
    5.59 -    private let encryptMode: EncryptMode
    5.60 -
    5.61 -    init(parentName: String = #function, appendFolderType: FolderType, imapSyncData: ImapSyncData,
    5.62 -                errorContainer: ServiceErrorProtocol = ErrorContainer(),
    5.63 -                encryptMode: EncryptMode) {
    5.64 -        targetFolderType = appendFolderType
    5.65 -        self.encryptMode = encryptMode
    5.66 -        super.init(parentName: parentName, errorContainer: errorContainer,
    5.67 -                   imapSyncData: imapSyncData)
    5.68 -    }
    5.69 -
    5.70 -    override public func main() {
    5.71 -        if !checkImapSync() {
    5.72 -            markAsFinished()
    5.73 -            return
    5.74 -        }
    5.75 -        syncDelegate = AppendMailsSyncDelegate(errorHandler: self)
    5.76 -        imapSyncData.sync?.delegate = syncDelegate
    5.77 -
    5.78 -        handleNextMessage()
    5.79 -    }
    5.80 -
    5.81 -    func retrieveNextMessage() -> (PEPMessageDict, PEPIdentity, NSManagedObjectID)? {
    5.82 -        Log.shared.errorAndCrash(component: #function,
    5.83 -                                 errorString: "Must be overridden in subclass")
    5.84 -        return nil
    5.85 -    }
    5.86 -
    5.87 -    private func retrieveFolderForAppend(
    5.88 -        account: CdAccount, context: NSManagedObjectContext) -> CdFolder? {
    5.89 -        return CdFolder.by(folderType: targetFolderType, account: account, context: context)
    5.90 -    }
    5.91 -
    5.92 -    func markLastMessageAsFinished() {
    5.93 -        if let msgID = lastHandledMessageObjectID {
    5.94 -            privateMOC.performAndWait { [weak self] in
    5.95 -                guard let theSelf = self else {
    5.96 -                    Log.shared.errorAndCrash(component: #function, errorString: "I got lost")
    5.97 -                    return
    5.98 -                }
    5.99 -                if let obj = theSelf.privateMOC.object(with: msgID) as? CdMessage {
   5.100 -                    if let msgID = obj.messageID {
   5.101 -                        theSelf.successAppendedMessageIDs.append(msgID)
   5.102 -                    }
   5.103 -                    theSelf.privateMOC.delete(obj)
   5.104 -                    theSelf.privateMOC.saveAndLogErrors()
   5.105 -                } else {
   5.106 -                    theSelf.handleError(
   5.107 -                        BackgroundError.GeneralError.invalidParameter(info: #function),
   5.108 -                        message: "Cannot find message just stored in the sent folder")
   5.109 -                    return
   5.110 -                }
   5.111 -            }
   5.112 -        }
   5.113 -    }
   5.114 -
   5.115 -    private func appendMessage(pEpMessageDict: PEPMessageDict?) {
   5.116 -        guard let msg = pEpMessageDict else {
   5.117 -            handleError(BackgroundError.GeneralError.invalidParameter(info: #function),
   5.118 -                        message: "Cannot append nil message")
   5.119 -            return
   5.120 -        }
   5.121 -        guard let folderName = targetFolderName else {
   5.122 -            Log.shared.errorAndCrash(component: #function, errorString: "No target")
   5.123 -            markAsFinished()
   5.124 -            return
   5.125 -        }
   5.126 -
   5.127 -        let pantMail = PEPUtil.pantomime(pEpMessageDict: msg)
   5.128 -        let folder = CWIMAPFolder(name: folderName)
   5.129 -        if let sync = imapSyncData.sync {
   5.130 -            folder.setStore(sync.imapStore)
   5.131 -        }
   5.132 -        guard let rawData = pantMail.dataValue() else {
   5.133 -            markAsFinished()
   5.134 -            return
   5.135 -        }
   5.136 -        let flags = targetFolderType.defaultAppendImapFlags()
   5.137 -        folder.appendMessage(fromRawSource: rawData,
   5.138 -                             flags: flags?.pantomimeFlags(),
   5.139 -                             internalDate: nil)
   5.140 -    }
   5.141 -
   5.142 -    func determineTargetFolder(msgID: NSManagedObjectID) {
   5.143 -        if targetFolderName != nil {
   5.144 -            // We already know the target folder, nothing to do
   5.145 -            return
   5.146 -        }
   5.147 -        privateMOC.performAndWait {
   5.148 -            guard let msg = self.privateMOC.object(with: msgID) as? CdMessage else {
   5.149 -                self.handleError(BackgroundError.GeneralError.invalidParameter(info: self.comp),
   5.150 -                                 message:
   5.151 -                    "Need a valid message for determining the sent folder name")
   5.152 -                return
   5.153 -            }
   5.154 -            guard let account = msg.parent?.account else {
   5.155 -                self.handleError(BackgroundError.GeneralError.invalidParameter(info: self.comp),
   5.156 -                                 message:
   5.157 -                    "Cannot append message without parent folder and this, account")
   5.158 -                return
   5.159 -            }
   5.160 -            guard let cdFolder = self.retrieveFolderForAppend(
   5.161 -                account: account, context: self.privateMOC) else {
   5.162 -                    self.handleError(
   5.163 -                        BackgroundError.GeneralError.invalidParameter(info: self.comp),
   5.164 -                        message: "Cannot find sent folder for message to append")
   5.165 -                    return
   5.166 -            }
   5.167 -            if cdFolder.folder().shouldNotAppendMessages {
   5.168 -                // We are not supposed to append messages to this (probably virtual) mailbox.
   5.169 -                // This is only for savety reasons, we should never come in here as messages
   5.170 -                // should not be marked for appending in the first place.
   5.171 -                // In case it turns out that there *Are* valid cases to reach this, we should
   5.172 -                // also delete the triggering message to avoid that it is processed here on
   5.173 -                // every sync loop.
   5.174 -                Log.shared.errorAndCrash(component: #function,
   5.175 -                                         errorString: "We should never come here.")
   5.176 -                handleNextMessage()
   5.177 -                return
   5.178 -            }
   5.179 -            guard let fn = cdFolder.name else {
   5.180 -                self.handleError(BackgroundError.GeneralError.invalidParameter(info: self.comp),
   5.181 -                                 message: "Need the name for the sent folder")
   5.182 -                return
   5.183 -            }
   5.184 -            self.targetFolderName = fn
   5.185 -        }
   5.186 -    }
   5.187 -
   5.188 -    func handleNextMessage() {
   5.189 -        markLastMessageAsFinished()
   5.190 -        guard !isCancelled else {
   5.191 -            waitForBackgroundTasksToFinish()
   5.192 -            return
   5.193 -        }
   5.194 -        guard let (msg, ident, objID) = retrieveNextMessage() else {
   5.195 -            markAsFinished()
   5.196 -            return
   5.197 -        }
   5.198 -        lastHandledMessageObjectID = objID
   5.199 -        determineTargetFolder(msgID: objID)
   5.200 -        let session = PEPSession()
   5.201 -        do {
   5.202 -            let (_, encMsg) = try encryptMode.encrypt(session: session, pEpMessageDict: msg,
   5.203 -                                                      forSelf: ident)
   5.204 -            appendMessage(pEpMessageDict: encMsg as? PEPMessageDict)
   5.205 -        } catch let err as NSError {
   5.206 -            handleError(err, message: "Cannot encrypt message")
   5.207 -        }
   5.208 -    }
   5.209 -
   5.210 -    override func markAsFinished() {
   5.211 -        syncDelegate = nil
   5.212 -        super.markAsFinished()
   5.213 -    }
   5.214 -}
   5.215 -
   5.216 -class AppendMailsSyncDelegate: DefaultImapSyncDelegate {
   5.217 -    public override func folderAppendCompleted(_ sync: ImapSync, notification: Notification?) {
   5.218 -        (errorHandler as? AppendMailsOperationBase)?.handleNextMessage()
   5.219 -    }
   5.220 -
   5.221 -    public override func folderAppendFailed(_ sync: ImapSync, notification: Notification?) {
   5.222 -        (errorHandler as? AppendMailsOperationBase)?.addIMAPError(ImapSyncError.folderAppendFailed)
   5.223 -        (errorHandler as? AppendMailsOperationBase)?.markAsFinished()
   5.224 -    }
   5.225 -}
     6.1 --- a/pEpForiOS/Background/AppendSendMailsOperation.swift	Wed Jul 04 11:24:51 2018 +0200
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,55 +0,0 @@
     6.4 -//
     6.5 -//  AppendSendMailsOperation.swift
     6.6 -//  pEpForiOS
     6.7 -//
     6.8 -//  Created by Andreas Buff on 30.08.17.
     6.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
    6.10 -//
    6.11 -
    6.12 -import MessageModel
    6.13 -import CoreData
    6.14 -
    6.15 -/**
    6.16 - Stores SMTPed mails in the sent folder.
    6.17 - */
    6.18 -public class AppendSendMailsOperation: AppendMailsOperationBase {
    6.19 -    public init(
    6.20 -        parentName: String = #function, imapSyncData: ImapSyncData,
    6.21 -        errorContainer: ServiceErrorProtocol = ErrorContainer()) {
    6.22 -        let appendFolder = FolderType.sent
    6.23 -        super.init(parentName: parentName, appendFolderType: appendFolder,
    6.24 -                   imapSyncData: imapSyncData, errorContainer: errorContainer,
    6.25 -                   encryptMode: .encryptAsOutgoing)
    6.26 -    }
    6.27 -
    6.28 -    override func retrieveNextMessage() -> (PEPMessageDict, PEPIdentity, NSManagedObjectID)? {
    6.29 -        var result:(PEPMessageDict, PEPIdentity, NSManagedObjectID)? = nil
    6.30 -        privateMOC.performAndWait {
    6.31 -            let p = NSPredicate(
    6.32 -                format: "uid = 0 and parent.folderTypeRawValue = %d and sendStatusRawValue = %d AND parent.account = %@",
    6.33 -                self.targetFolderType.rawValue, SendStatus.smtpDone.rawValue,
    6.34 -                imapSyncData.connectInfo.accountObjectID)
    6.35 -            guard
    6.36 -                let msg = CdMessage.first(predicate: p, in: self.privateMOC),
    6.37 -                let cdIdent = msg.parent?.account?.identity,
    6.38 -                let folder = msg.parent?.folder() else {
    6.39 -                    result = nil
    6.40 -                    return
    6.41 -            }
    6.42 -
    6.43 -            if folder.shouldNotAppendMessages {
    6.44 -                // For this folder the server appends send mails.
    6.45 -                // To avoid the current msg is processed here on every sync loop, we delete it.
    6.46 -                // That should be save as the message has been send successfully
    6.47 -                // (SendStatus.smtpDone) and the server is responsible for appending sent mails.
    6.48 -                msg.delete()
    6.49 -                Record.saveAndWait(context: privateMOC)
    6.50 -                // Recursivly get the next message.
    6.51 -                result = retrieveNextMessage()
    6.52 -                return
    6.53 -            }
    6.54 -            result = (msg.pEpMessageDict(), cdIdent.pEpIdentity(), msg.objectID)
    6.55 -        }
    6.56 -        return result
    6.57 -    }
    6.58 -}
     7.1 --- a/pEpForiOS/Background/DecryptMessagesOperation.swift	Wed Jul 04 11:24:51 2018 +0200
     7.2 +++ b/pEpForiOS/Background/DecryptMessagesOperation.swift	Wed Jul 04 11:27:58 2018 +0200
     7.3 @@ -21,6 +21,7 @@
     7.4  
     7.5  public class DecryptMessagesOperation: ConcurrentBaseOperation {
     7.6      public weak var delegate: DecryptMessagesOperationDelegateProtocol?// Only used in Tests. Maybe refactor out.
     7.7 +    private(set) var didMarkMessagesForReUpload = false
     7.8  
     7.9      public override func main() {
    7.10          if isCancelled {
    7.11 @@ -44,11 +45,15 @@
    7.12              }
    7.13  
    7.14              for cdMessage in cdMessages {
    7.15 +                guard let message = cdMessage.message() else {
    7.16 +                    Log.shared.errorAndCrash(component: #function, errorString: "No message")
    7.17 +                    continue
    7.18 +                }
    7.19                  if me.isCancelled {
    7.20                      break
    7.21                  }
    7.22                  //
    7.23 -                let originalRating = cdMessage.pEpRating
    7.24 +                let ratingBeforeEngine = cdMessage.pEpRating
    7.25  
    7.26                  var outgoing = false
    7.27                  if let folderType = cdMessage.parent?.folderType {
    7.28 @@ -63,81 +68,120 @@
    7.29                  let session = PEPSession()
    7.30  
    7.31                  var rating = PEP_rating_undefined
    7.32 +                let pEpDecryptedMessage: NSDictionary
    7.33                  do {
    7.34 -                    let pEpDecryptedMessage = try session.decryptMessageDict(
    7.35 -                        pepMessage, flags: nil, rating: &rating, extraKeys: &keys, status: nil)
    7.36 -                        as NSDictionary
    7.37 -                    handleDecryptionSuccess(cdMessage: cdMessage,
    7.38 -                                            pEpDecryptedMessage: pEpDecryptedMessage,
    7.39 -                                            originalRating: originalRating,
    7.40 -                                            rating: rating,
    7.41 -                                            keys: keys)
    7.42 +                    // (This nasty if clause is a workaround to what I consider as a Swift 4.1 bug,
    7.43 +                    // causing an error "generic parameter "wrapped" could not be inferred".
    7.44 +                    // The only difference is the `flags`parameter.)
    7.45 +                    if message.isOnTrustedServer {
    7.46 +                        pEpDecryptedMessage = try session.decryptMessageDict(pepMessage,
    7.47 +                                                                             flags: nil,
    7.48 +                                                                             rating: &rating,
    7.49 +                                                                             extraKeys: &keys,
    7.50 +                                                                             status: nil)
    7.51 +                            as NSDictionary
    7.52 +                    } else {
    7.53 +                        var flags = PEP_decrypt_flag_untrusted_server
    7.54 +                        pEpDecryptedMessage = try session.decryptMessageDict(pepMessage,
    7.55 +                                                                             flags: &flags,
    7.56 +                                                                             rating: &rating,
    7.57 +                                                                             extraKeys: &keys,
    7.58 +                                                                             status: nil)
    7.59 +                            as NSDictionary
    7.60 +                    }
    7.61 +                    me.handleDecryptionSuccess(cdMessage: cdMessage,
    7.62 +                                               pEpDecryptedMessage: pEpDecryptedMessage,
    7.63 +                                               ratingBeforeEngine: ratingBeforeEngine,
    7.64 +                                               rating: rating,
    7.65 +                                               keys: keys)
    7.66                  } catch let error as NSError {
    7.67                      // log, and try again next time
    7.68                      Log.error(component: #function, error: error)
    7.69                  }
    7.70              }
    7.71          }
    7.72 +    }
    7.73  
    7.74 -        func handleDecryptionSuccess(cdMessage: CdMessage,
    7.75 -                                     pEpDecryptedMessage: NSDictionary,
    7.76 -                                     originalRating: Int16,
    7.77 -                                     rating: PEP_rating,
    7.78 -                                     keys: NSArray?) {
    7.79 -            let theKeys = Array(keys ?? NSArray()) as? [String] ?? []
    7.80 +    private func handleDecryptionSuccess(cdMessage: CdMessage,
    7.81 +                                         pEpDecryptedMessage: NSDictionary,
    7.82 +                                         ratingBeforeEngine: Int16,
    7.83 +                                         rating: PEP_rating,
    7.84 +                                         keys: NSArray?) {
    7.85 +        let theKeys = Array(keys ?? NSArray()) as? [String] ?? []
    7.86  
    7.87 -            self.delegate?.decrypted(
    7.88 -                originalCdMessage: cdMessage, decryptedMessageDict: pEpDecryptedMessage,
    7.89 -                rating: rating, keys: theKeys) // Only used in Tests. Maybe refactor out.
    7.90 +        // Only used in Tests. Maybe refactor out.
    7.91 +        self.delegate?.decrypted(originalCdMessage: cdMessage,
    7.92 +                                 decryptedMessageDict: pEpDecryptedMessage,
    7.93 +                                 rating: rating,
    7.94 +                                 keys: theKeys)
    7.95  
    7.96 -            updateWholeMessage(
    7.97 -                pEpDecryptedMessage: pEpDecryptedMessage,
    7.98 -                originalRating: originalRating,
    7.99 -                rating: rating,
   7.100 -                cdMessage: cdMessage,
   7.101 -                keys: theKeys,
   7.102 -                context: context)
   7.103 +        if rating.shouldUpdateMessageContent() {
   7.104 +            updateWholeMessage(pEpDecryptedMessage: pEpDecryptedMessage,
   7.105 +                               ratingBeforeEngine: ratingBeforeEngine,
   7.106 +                               rating: rating,
   7.107 +                               cdMessage: cdMessage,
   7.108 +                               keys: theKeys)
   7.109 +            handleReUploadAndNotify(cdMessage: cdMessage, rating: rating)
   7.110 +        } else {
   7.111 +            if rating.rawValue != ratingBeforeEngine {
   7.112 +                cdMessage.update(rating: rating)
   7.113 +                saveAndNotify(cdMessage: cdMessage  )
   7.114 +            }
   7.115          }
   7.116      }
   7.117  
   7.118      /**
   7.119       Updates message bodies (after decryption), then calls `updateMessage`.
   7.120       */
   7.121 -    func updateWholeMessage(
   7.122 -        pEpDecryptedMessage: NSDictionary?,
   7.123 -        originalRating: Int16,
   7.124 -        rating: PEP_rating,
   7.125 -        cdMessage: CdMessage, keys: [String],
   7.126 -        context: NSManagedObjectContext) {
   7.127 +    private func updateWholeMessage(pEpDecryptedMessage: NSDictionary?,
   7.128 +                                    ratingBeforeEngine: Int16,
   7.129 +                                    rating: PEP_rating,
   7.130 +                                    cdMessage: CdMessage,
   7.131 +                                    keys: [String]) {
   7.132          cdMessage.underAttack = rating.isUnderAttack()
   7.133 -        if rating.shouldUpdateMessageContent() {
   7.134 -            guard let decrypted = pEpDecryptedMessage as? PEPMessageDict else {
   7.135 -                Log.shared.errorAndCrash(
   7.136 -                    component: #function,
   7.137 -                    errorString:"should update message with rating \(rating), but nil message")
   7.138 -                return
   7.139 +        guard let decrypted = pEpDecryptedMessage as? PEPMessageDict else {
   7.140 +            Log.shared.errorAndCrash(
   7.141 +                component: #function,
   7.142 +                errorString:"should update message with rating \(rating), but nil message")
   7.143 +            return
   7.144 +        }
   7.145 +        updateMessage(cdMessage: cdMessage, keys: keys, pEpMessageDict: decrypted, rating: rating)
   7.146 +    }
   7.147 +
   7.148 +    private func handleReUploadAndNotify(cdMessage: CdMessage, rating: PEP_rating) {
   7.149 +        do {
   7.150 +            let needsReUpload = try handleReUploadIfRequired(cdMessage: cdMessage, rating: rating)
   7.151 +            if needsReUpload {
   7.152 +                didMarkMessagesForReUpload = true
   7.153 +                Record.saveAndWait()
   7.154 +                privateMOC.saveAndLogErrors()
   7.155 +                // Don't notify. Delegate will be notified after the re-uploaded message is fetched.
   7.156 +            } else {
   7.157 +                saveAndNotify(cdMessage: cdMessage)
   7.158              }
   7.159 -            cdMessage.update(pEpMessageDict: decrypted, rating: rating)
   7.160 -            updateMessage(cdMessage: cdMessage, keys: keys, context: context)
   7.161 -        } else {
   7.162 -            if rating.rawValue != originalRating {
   7.163 -                cdMessage.update(rating: rating)
   7.164 -                saveAndNotify(cdMessage: cdMessage, context: context)
   7.165 -            }
   7.166 +        } catch {
   7.167 +            handleError(error)
   7.168          }
   7.169      }
   7.170  
   7.171 -    func saveAndNotify(cdMessage: CdMessage, context: NSManagedObjectContext) {
   7.172 -        context.saveAndLogErrors()
   7.173 +    private func saveAndNotify(cdMessage: CdMessage) {
   7.174 +        privateMOC.saveAndLogErrors()
   7.175          notifyDelegate(messageUpdated: cdMessage)
   7.176      }
   7.177  
   7.178 -    /**
   7.179 -     Updates the given key list for the message and notifies delegates.
   7.180 -     */
   7.181 -    func updateMessage(cdMessage: CdMessage, keys: [String], context: NSManagedObjectContext) {
   7.182 +    /// Updates a message with the given data.
   7.183 +    ///
   7.184 +    /// - Parameters:
   7.185 +    ///   - cdMessage: message to update
   7.186 +    ///   - keys: keys the message has been signed with
   7.187 +    ///   - pEpMessageDict: decrypted message
   7.188 +    ///   - rating: rating to set
   7.189 +    private func updateMessage(cdMessage: CdMessage,
   7.190 +                               keys: [String],
   7.191 +                               pEpMessageDict: PEPMessageDict,
   7.192 +                               rating: PEP_rating) {
   7.193 +        cdMessage.update(pEpMessageDict: pEpMessageDict, rating: rating)
   7.194          cdMessage.updateKeyList(keys: keys)
   7.195 -        saveAndNotify(cdMessage: cdMessage, context: context)
   7.196      }
   7.197  
   7.198      private func notifyDelegate(messageUpdated cdMessage: CdMessage) {
   7.199 @@ -148,3 +192,43 @@
   7.200          MessageModelConfig.messageFolderDelegate?.didCreate(messageFolder: message)
   7.201      }
   7.202  }
   7.203 +
   7.204 +// MARK: - Re-Upload - Trusted Server
   7.205 +
   7.206 +extension DecryptMessagesOperation {
   7.207 +    private func handleReUploadIfRequired(cdMessage: CdMessage,
   7.208 +                                          rating: PEP_rating) throws -> Bool {
   7.209 +        guard let message = cdMessage.message() else {
   7.210 +            Log.shared.errorAndCrash(component: #function, errorString: "No Message")
   7.211 +            throw BackgroundError.GeneralError.illegalState(info: "No Message")
   7.212 +        }
   7.213 +        if !message.isOnTrustedServer ||    // The only currently supported case for re-upload is trusted server.
   7.214 +            message.wasAlreadyUnencrypted { // If the message was not encrypted, there is no reason to re-upload it.
   7.215 +            return false
   7.216 +        }
   7.217 +        let messageCopyForReupload = Message(message: message)
   7.218 +        setOriginalRatingHeader(rating: rating, toMessage: messageCopyForReupload)
   7.219 +        message.imapMarkDeleted()
   7.220 +
   7.221 +        return true
   7.222 +    }
   7.223 +
   7.224 +    private func setOriginalRatingHeader(rating: PEP_rating, toMessage cdMessage: CdMessage) {
   7.225 +        guard let message = cdMessage.message() else {
   7.226 +            Log.shared.errorAndCrash(component: #function, errorString: "No Message")
   7.227 +            handleError(BackgroundError.GeneralError.illegalState(info: "No Message"))
   7.228 +            return
   7.229 +        }
   7.230 +        if message.parent.folderType == .drafts {
   7.231 +            let outgoingRating = message.outgoingMessageRating()
   7.232 +            setOriginalRatingHeader(rating: outgoingRating, toMessage: message)
   7.233 +        } else {
   7.234 +            setOriginalRatingHeader(rating: rating, toMessage: message)
   7.235 +        }
   7.236 +    }
   7.237 +
   7.238 +    private func setOriginalRatingHeader(rating: PEP_rating, toMessage msg: Message) {
   7.239 +        msg.setOriginalRatingHeader(rating: rating)
   7.240 +        msg.save()
   7.241 +    }
   7.242 +}
     8.1 --- a/pEpForiOS/Background/EncryptAndSendOperation.swift	Wed Jul 04 11:24:51 2018 +0200
     8.2 +++ b/pEpForiOS/Background/EncryptAndSendOperation.swift	Wed Jul 04 11:27:58 2018 +0200
     8.3 @@ -118,8 +118,8 @@
     8.4                      content: "Setting \(String(describing: msg.messageID)): \(msg.sendStatus)")
     8.5                  context.saveAndLogErrors()
     8.6              } else {
     8.7 -                Log.error(
     8.8 -                    component: self.comp, errorString: "Could not access sent message by ID")
     8.9 +                Log.shared.errorAndCrash(component: #function,
    8.10 +                                         errorString: "Could not access sent message by ID")
    8.11              }
    8.12          }
    8.13      }
    8.14 @@ -145,14 +145,16 @@
    8.15          }
    8.16  
    8.17          lastSentMessageObjectID = nil
    8.18 -        if let (msg, protected, objID) = EncryptAndSendOperation.retrieveNextMessage(
    8.19 +        if let (msg, protected, cdMessageObjID) = EncryptAndSendOperation.retrieveNextMessage(
    8.20              context: context, cdAccount: cdAccount) {
    8.21 -            lastSentMessageObjectID = objID
    8.22 +            lastSentMessageObjectID = cdMessageObjID
    8.23              let session = PEPSession()
    8.24              do {
    8.25 -                let (_, encMsg) = try session.encrypt(
    8.26 +                let (_, encryptedMessageToSend) = try session.encrypt(
    8.27                      pEpMessageDict: msg, encryptionFormat: protected ? PEP_enc_PEP : PEP_enc_none)
    8.28 -                send(pEpMessageDict: encMsg as? PEPMessageDict)
    8.29 +
    8.30 +                setOriginalRatingHeader(toMessageWithObjId: cdMessageObjID, inContext: context)
    8.31 +                send(pEpMessageDict: encryptedMessageToSend as? PEPMessageDict)
    8.32              } catch let err as NSError {
    8.33                  handleError(err)
    8.34              }
    8.35 @@ -160,6 +162,19 @@
    8.36              markAsFinished()
    8.37          }
    8.38      }
    8.39 +
    8.40 +    private func setOriginalRatingHeader(toMessageWithObjId objId: NSManagedObjectID,
    8.41 +                                         inContext moc: NSManagedObjectContext) {
    8.42 +        guard let unencryptedMessage = CdMessage.message(withObjectID: objId) else {
    8.43 +                Log.shared.errorAndCrash(component: #function, errorString: "No Message")
    8.44 +                handleError(BackgroundError.GeneralError.illegalState(info: "No Message"))
    8.45 +                return
    8.46 +        }
    8.47 +
    8.48 +        let originalRating = unencryptedMessage.outgoingMessageRating()
    8.49 +        unencryptedMessage.setOriginalRatingHeader(rating: originalRating)
    8.50 +        unencryptedMessage.save()
    8.51 +    }
    8.52  }
    8.53  
    8.54  extension EncryptAndSendOperation: SmtpSendDelegate {
     9.1 --- a/pEpForiOS/Error/DisplayUserError.swift	Wed Jul 04 11:24:51 2018 +0200
     9.2 +++ b/pEpForiOS/Error/DisplayUserError.swift	Wed Jul 04 11:27:58 2018 +0200
     9.3 @@ -264,7 +264,7 @@
     9.4                                       comment:
     9.5                  "Error message shown to the user in case a message could not be sent.")
     9.6          case .brokenServerConnection:
     9.7 -            return NSLocalizedString("We could not connect to the server.)",
     9.8 +            return NSLocalizedString("We could not connect to the server.",
     9.9                                       comment:
    9.10                  "Error message shown to the user in case we can not connect to the server")
    9.11          case .internalError:
    10.1 --- a/pEpForiOS/Models/Message+pEp.swift	Wed Jul 04 11:24:51 2018 +0200
    10.2 +++ b/pEpForiOS/Models/Message+pEp.swift	Wed Jul 04 11:27:58 2018 +0200
    10.3 @@ -13,35 +13,67 @@
    10.4          return PEPUtil.pEpRatingFromInt(self.pEpRatingInt) == PEP_rating_undefined
    10.5      }
    10.6  
    10.7 +    public var wasAlreadyUnencrypted: Bool {
    10.8 +        return PEPUtil.pEpRatingFromInt(self.pEpRatingInt) == PEP_rating_unencrypted
    10.9 +    }
   10.10 +
   10.11 +    // @objc to make it swizzle-able in tests
   10.12 +    @objc public var isOnTrustedServer: Bool {
   10.13 +        return parent.account.server(with: .imap)?.trusted ?? false
   10.14 +    }
   10.15 +
   10.16      public func pEpMessageDict(outgoing: Bool = true) -> PEPMessageDict {
   10.17          return PEPUtil.pEpDict(message: self)
   10.18      }
   10.19  
   10.20 -    public func pEpRating(session: PEPSession = PEPSession()) -> PEP_rating {
   10.21 -        if belongToSentFolder() || belongToDraftFolder () || belongToTrashFolder() {
   10.22 -            if let original = self.optionalFields[Headers.originalRating.rawValue] {
   10.23 -                return session.rating(from: original)
   10.24 +    func outgoingMessageRating() -> PEP_rating {
   10.25 +        guard let sender = from else {
   10.26 +            Log.shared.errorAndCrash(component: #function,
   10.27 +                                     errorString: "No sender for outgoing message?")
   10.28 +            return PEP_rating_undefined
   10.29 +        }
   10.30 +        return PEPSession().outgoingMessageRating(from:sender, to:to, cc:cc, bcc:bcc)
   10.31 +    }
   10.32 +
   10.33 +    func getOriginalRatingHeader() -> String? {
   10.34 +            return optionalFields[Headers.originalRating.rawValue]
   10.35 +    }
   10.36 +
   10.37 +    func getOriginalRatingHeaderRating() -> PEP_rating? {
   10.38 +        guard let originalRatingStr = getOriginalRatingHeader() else {
   10.39 +            return nil
   10.40 +        }
   10.41 +        return PEP_rating.fromString(str: originalRatingStr)
   10.42 +    }
   10.43 +
   10.44 +    private func setOriginalRatingHeader(rating: String) {
   10.45 +        return optionalFields[Headers.originalRating.rawValue] = rating
   10.46 +    }
   10.47 +
   10.48 +    func setOriginalRatingHeader(rating: PEP_rating) {
   10.49 +        return setOriginalRatingHeader(rating: rating.asString())
   10.50 +    }
   10.51 +
   10.52 +    public func pEpRating() -> PEP_rating {
   10.53 +        //see: https://dev.pep.security/Common%20App%20Documentation/algorithms/MessageColors
   10.54 +        if let originalRating = getOriginalRatingHeaderRating() {
   10.55 +            switch parent.folderType {
   10.56 +            case .sent, .trash, .drafts:
   10.57 +                return originalRating
   10.58 +            case .all, .archive, .inbox, .normal, .spam, .flagged:
   10.59 +                if isOnTrustedServer {
   10.60 +                    return originalRating
   10.61 +                } else {
   10.62 +                    return PEPUtil.pEpRatingFromInt(pEpRatingInt) ?? PEP_rating_undefined
   10.63 +                }
   10.64              }
   10.65 -            return PEP_rating_undefined
   10.66          } else {
   10.67              return PEPUtil.pEpRatingFromInt(pEpRatingInt) ?? PEP_rating_undefined
   10.68          }
   10.69      }
   10.70  
   10.71 -    public func pEpColor(session: PEPSession = PEPSession()) -> PEP_color {
   10.72 -        return pEpRating(session: session).pEpColor()
   10.73 -    }
   10.74 -
   10.75 -    func belongToSentFolder() -> Bool {
   10.76 -        return self.parent.folderType == FolderType.sent
   10.77 -    }
   10.78 -    
   10.79 -    func belongToDraftFolder() -> Bool {
   10.80 -        return self.parent.folderType == FolderType.drafts
   10.81 -    }
   10.82 -
   10.83 -    func belongToTrashFolder() -> Bool {
   10.84 -        return self.parent.folderType == FolderType.trash
   10.85 +    public func pEpColor() -> PEP_color {
   10.86 +        return pEpRating().pEpColor()
   10.87      }
   10.88  
   10.89      /**
   10.90 @@ -88,6 +120,21 @@
   10.91          }
   10.92          return result
   10.93      }
   10.94 +
   10.95 +    static public func allMessagesMarkedForAppend(inAccount account: Account) -> [Message] {
   10.96 +        let p = CdMessage.PredicateFactory.needImapAppend(inAccountWithAddress: account.user.address)
   10.97 +        let cdMessages = CdMessage.all(predicate: p) as? [CdMessage] ?? []
   10.98 +        var result = [Message]()
   10.99 +        for cdMessage in cdMessages {
  10.100 +            guard let message = cdMessage.message() else {
  10.101 +                Log.shared.errorAndCrash(component: #function,
  10.102 +                                         errorString: "No Message for CdMesssage")
  10.103 +                continue
  10.104 +            }
  10.105 +            result.append(message)
  10.106 +        }
  10.107 +        return result
  10.108 +    }
  10.109  }
  10.110  
  10.111  // MARK: - Handshake
  10.112 @@ -111,39 +158,37 @@
  10.113       one can handshake on.
  10.114       - Returns: A list of `HandshakeCombination`s.
  10.115       */
  10.116 -    public static func handshakeActionCombinations<T>(
  10.117 -        session: PEPSession = PEPSession(),
  10.118 -        identities: T) -> [HandshakeCombination]
  10.119 +    public static func handshakeActionCombinations<T>(identities: T) -> [HandshakeCombination]
  10.120          where T: Collection, T.Element: Identity {
  10.121 -        let ownIdentities = identities.filter() {
  10.122 -            $0.isMySelf
  10.123 -        }
  10.124 +            let session = PEPSession()
  10.125 +            let ownIdentities = identities.filter() {
  10.126 +                $0.isMySelf
  10.127 +            }
  10.128  
  10.129 -        let ownIdentitiesWithKeys = ownIdentities.filter() {
  10.130 -            (try? $0.fingerPrint(session: session)) != nil
  10.131 -        }
  10.132 +            let ownIdentitiesWithKeys = ownIdentities.filter() {
  10.133 +                (try? $0.fingerPrint(session: session)) != nil
  10.134 +            }
  10.135  
  10.136 -        let partnerIdenties = identities.filter() {
  10.137 -            $0.canInvokeHandshakeAction(session: session)
  10.138 -        }
  10.139 +            let partnerIdenties = identities.filter() {
  10.140 +                $0.canInvokeHandshakeAction(session: session)
  10.141 +            }
  10.142  
  10.143 -        var handshakable = [HandshakeCombination]()
  10.144 -        for ownId in ownIdentitiesWithKeys {
  10.145 -            for partnerId in partnerIdenties {
  10.146 -                handshakable.append(HandshakeCombination(
  10.147 -                    ownIdentity: ownId, partnerIdentity: partnerId))
  10.148 +            var handshakable = [HandshakeCombination]()
  10.149 +            for ownId in ownIdentitiesWithKeys {
  10.150 +                for partnerId in partnerIdenties {
  10.151 +                    handshakable.append(HandshakeCombination(
  10.152 +                        ownIdentity: ownId, partnerIdentity: partnerId))
  10.153 +                }
  10.154              }
  10.155 -        }
  10.156  
  10.157 -        return handshakable
  10.158 +            return handshakable
  10.159      }
  10.160  
  10.161      /**
  10.162       Determines all possible handshake combinations that the identies referenced in a message
  10.163       represent.
  10.164       */
  10.165 -    public func handshakeActionCombinations(
  10.166 -        session: PEPSession = PEPSession()) -> [HandshakeCombination] {
  10.167 -        return Message.handshakeActionCombinations(session: session, identities: allIdentities)
  10.168 +    public func handshakeActionCombinations() -> [HandshakeCombination] {
  10.169 +        return Message.handshakeActionCombinations(identities: allIdentities)
  10.170      }
  10.171  }
    11.1 --- a/pEpForiOS/Network/Service/MessageSyncService/AtomicImapService.swift	Wed Jul 04 11:24:51 2018 +0200
    11.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.3 @@ -1,47 +0,0 @@
    11.4 -//
    11.5 -//  AtomicImapService.swift
    11.6 -//  pEpForiOS
    11.7 -//
    11.8 -//  Created by Dirk Zimmermann on 03.07.17.
    11.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   11.10 -//
   11.11 -
   11.12 -import Foundation
   11.13 -
   11.14 -typealias ServiceFinishedHandler = (_ error: Error?) -> ()
   11.15 -
   11.16 -protocol ServiceExecutionProtocol {
   11.17 -    func cancel()
   11.18 -    func execute(handler: ServiceFinishedHandler?)
   11.19 -}
   11.20 -
   11.21 -class AtomicImapService: ServiceErrorProtocol {
   11.22 -    let backgroundQueue = OperationQueue()
   11.23 -
   11.24 -    let parentName: String
   11.25 -    let backgrounder: BackgroundTaskProtocol?
   11.26 -    
   11.27 -    init(parentName: String = #function, backgrounder: BackgroundTaskProtocol? = nil) {
   11.28 -        self.parentName = parentName
   11.29 -        self.backgrounder = backgrounder
   11.30 -    }
   11.31 -
   11.32 -    func handle(error: Error, taskID: BackgroundTaskID?, handler: ServiceFinishedHandler?) {
   11.33 -        backgrounder?.endBackgroundTask(taskID)
   11.34 -        handler?(error)
   11.35 -    }
   11.36 -
   11.37 -    // MARK: - ServiceErrorProtocol
   11.38 -
   11.39 -    private(set) public var error: Error?
   11.40 -
   11.41 -    public func addError(_ error: Error) {
   11.42 -        if self.error == nil {
   11.43 -            self.error = error
   11.44 -        }
   11.45 -    }
   11.46 -
   11.47 -    public func hasErrors() -> Bool {
   11.48 -        return error != nil
   11.49 -    }
   11.50 -}
    12.1 --- a/pEpForiOS/Network/Service/MessageSyncService/BackgroundOperationImapService.swift	Wed Jul 04 11:24:51 2018 +0200
    12.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.3 @@ -1,23 +0,0 @@
    12.4 -//
    12.5 -//  BackgroundOperationImapService.swift
    12.6 -//  pEpForiOS
    12.7 -//
    12.8 -//  Created by Dirk Zimmermann on 27.07.17.
    12.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   12.10 -//
   12.11 -
   12.12 -import Foundation
   12.13 -
   12.14 -class BackgroundOperationImapService: AtomicImapService {
   12.15 -    let imapSyncData: ImapSyncData
   12.16 -
   12.17 -    init(parentName: String = #function, backgrounder: BackgroundTaskProtocol? = nil,
   12.18 -         imapSyncData: ImapSyncData) {
   12.19 -        self.imapSyncData = imapSyncData
   12.20 -        super.init(parentName: parentName, backgrounder: backgrounder)
   12.21 -    }
   12.22 -
   12.23 -    func cancel() {
   12.24 -        backgroundQueue.cancelAllOperations()
   12.25 -    }
   12.26 -}
    13.1 --- a/pEpForiOS/Network/Service/MessageSyncService/FetchMessagesService.swift	Wed Jul 04 11:24:51 2018 +0200
    13.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.3 @@ -1,49 +0,0 @@
    13.4 -//
    13.5 -//  FetchMessagesService.swift
    13.6 -//  pEpForiOS
    13.7 -//
    13.8 -//  Created by Dirk Zimmermann on 05.07.17.
    13.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   13.10 -//
   13.11 -
   13.12 -import Foundation
   13.13 -
   13.14 -import MessageModel
   13.15 -
   13.16 -protocol FetchMessagesServiceDelegate: class {
   13.17 -    func didFetch(message: Message)
   13.18 -}
   13.19 -
   13.20 -class FetchMessagesService: BackgroundOperationImapService {
   13.21 -    weak var delegate: FetchMessagesServiceDelegate?
   13.22 -    let folderName: String
   13.23 -
   13.24 -    init(parentName: String = #function, backgrounder: BackgroundTaskProtocol? = nil,
   13.25 -                  imapSyncData: ImapSyncData,
   13.26 -                  folderName: String = ImapSync.defaultImapInboxName) {
   13.27 -        self.folderName = folderName
   13.28 -        super.init(parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData)
   13.29 -    }
   13.30 -}
   13.31 -
   13.32 -extension FetchMessagesService: ServiceExecutionProtocol {
   13.33 -    func execute(handler: ServiceFinishedHandler? = nil) {
   13.34 -        let bgID = backgrounder?.beginBackgroundTask(taskName: "FetchMessagesService")
   13.35 -        let loginOp = LoginImapOperation(
   13.36 -            parentName: parentName, errorContainer: self, imapSyncData: imapSyncData)
   13.37 -        let fetchOp = FetchMessagesOperation(
   13.38 -            parentName: parentName, errorContainer: self, imapSyncData: imapSyncData,
   13.39 -            folderName: folderName) { [weak self] cdMessage in
   13.40 -                if let del = self?.delegate, let msg = cdMessage.message() {
   13.41 -                    del.didFetch(message: msg)
   13.42 -                }
   13.43 -        }
   13.44 -        fetchOp.addDependency(loginOp)
   13.45 -        fetchOp.completionBlock = { [weak self] in
   13.46 -            fetchOp.completionBlock = nil
   13.47 -            self?.backgrounder?.endBackgroundTask(bgID)
   13.48 -            handler?(self?.error)
   13.49 -        }
   13.50 -        backgroundQueue.addOperations([loginOp, fetchOp], waitUntilFinished: false)
   13.51 -    }
   13.52 -}
    14.1 --- a/pEpForiOS/Network/Service/MessageSyncService/ImapIdleService.swift	Wed Jul 04 11:24:51 2018 +0200
    14.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.3 @@ -1,90 +0,0 @@
    14.4 -//
    14.5 -//  ImapIdleService.swift
    14.6 -//  pEpForiOS
    14.7 -//
    14.8 -//  Created by Dirk Zimmermann on 13.07.17.
    14.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   14.10 -//
   14.11 -
   14.12 -import Foundation
   14.13 -
   14.14 -protocol ImapIdleServiceDelegate: class {
   14.15 -    func didEnterIdle(service: ImapIdleService)
   14.16 -}
   14.17 -
   14.18 -class ImapIdleService: AtomicImapService {
   14.19 -    enum IdleResult {
   14.20 -        case nothing
   14.21 -        case error
   14.22 -        case newMessages
   14.23 -        case idleExit
   14.24 -    }
   14.25 -
   14.26 -    weak var delegate: ImapIdleServiceDelegate?
   14.27 -    var idleResult: IdleResult = .nothing
   14.28 -    let imapSyncData: ImapSyncData
   14.29 -
   14.30 -    var handler: ServiceFinishedHandler?
   14.31 -    var syncDelegate: DefaultImapSyncDelegate?
   14.32 -
   14.33 -    init(parentName: String = #function, backgrounder: BackgroundTaskProtocol? = nil,
   14.34 -         imapSyncData: ImapSyncData) {
   14.35 -        self.imapSyncData = imapSyncData
   14.36 -        super.init(parentName: parentName, backgrounder: backgrounder)
   14.37 -        syncDelegate = ImapIdleSyncDelegate(errorHandler: self)
   14.38 -    }
   14.39 -
   14.40 -    func handleNewMessages() {
   14.41 -        idleResult = .newMessages
   14.42 -        handler?(nil)
   14.43 -    }
   14.44 -}
   14.45 -
   14.46 -extension ImapIdleService: ServiceExecutionProtocol {
   14.47 -    func cancel() {
   14.48 -        imapSyncData.sync?.delegate = nil
   14.49 -        self.handler = nil
   14.50 -    }
   14.51 -
   14.52 -    func execute(handler: ServiceFinishedHandler?) {
   14.53 -        self.handler = handler
   14.54 -        imapSyncData.sync?.delegate = syncDelegate
   14.55 -        imapSyncData.sync?.sendIdle()
   14.56 -    }
   14.57 -
   14.58 -    func didEnterIdle() {
   14.59 -        delegate?.didEnterIdle(service: self)
   14.60 -    }
   14.61 -
   14.62 -    func didFinishIdle() {
   14.63 -        idleResult = .idleExit
   14.64 -        handler?(nil)
   14.65 -    }
   14.66 -}
   14.67 -
   14.68 -extension ImapIdleService: ImapSyncDelegateErrorHandlerProtocol {
   14.69 -    func handle(error: Error) {
   14.70 -        addError(error)
   14.71 -        idleResult = .error
   14.72 -        handler?(error)
   14.73 -    }
   14.74 -}
   14.75 -
   14.76 -class ImapIdleSyncDelegate: DefaultImapSyncDelegate {
   14.77 -    override func idleNewMessages(_ sync: ImapSync, notification: Notification?) {
   14.78 -        (errorHandler as? ImapIdleService)?.handleNewMessages()
   14.79 -    }
   14.80 -
   14.81 -    override func messageChanged(_ sync: ImapSync, notification: Notification?) {
   14.82 -        // Will be called when the status of existing messages changes.
   14.83 -        // Can be ignored since the storing is already handled by PersistentImapFolder.
   14.84 -    }
   14.85 -
   14.86 -    override func idleEntered(_ sync: ImapSync, notification: Notification?) {
   14.87 -        (errorHandler as? ImapIdleService)?.didEnterIdle()
   14.88 -    }
   14.89 -
   14.90 -    override func idleFinished(_ sync: ImapSync, notification: Notification?) {
   14.91 -        (errorHandler as? ImapIdleService)?.didFinishIdle()
   14.92 -    }
   14.93 -}
    15.1 --- a/pEpForiOS/Network/Service/MessageSyncService/ImapSmtpConnection.swift	Wed Jul 04 11:24:51 2018 +0200
    15.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.3 @@ -1,28 +0,0 @@
    15.4 -//
    15.5 -//  ImapSmtpConnection.swift
    15.6 -//  pEpForiOS
    15.7 -//
    15.8 -//  Created by Dirk Zimmermann on 29.06.17.
    15.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   15.10 -//
   15.11 -
   15.12 -import Foundation
   15.13 -
   15.14 -/**
   15.15 - Encapsulates an IMAP/SMTP connection, can be used as a key in dictionaries.
   15.16 - */
   15.17 -struct ImapSmtpConnection: Hashable {
   15.18 -    let imapConnectInfo: EmailConnectInfo
   15.19 -    let smtpConnectInfo: EmailConnectInfo
   15.20 -
   15.21 -    var hashValue: Int {
   15.22 -        return imapConnectInfo.hashValue &+ smtpConnectInfo.hashValue
   15.23 -    }
   15.24 -}
   15.25 -
   15.26 -extension ImapSmtpConnection: Equatable {
   15.27 -    static func ==(lhs: ImapSmtpConnection, rhs: ImapSmtpConnection) -> Bool {
   15.28 -        return lhs.imapConnectInfo == rhs.imapConnectInfo &&
   15.29 -            lhs.smtpConnectInfo == rhs.smtpConnectInfo
   15.30 -    }
   15.31 -}
    16.1 --- a/pEpForiOS/Network/Service/MessageSyncService/ImapSmtpSyncService.swift	Wed Jul 04 11:24:51 2018 +0200
    16.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.3 @@ -1,375 +0,0 @@
    16.4 -//
    16.5 -//  ImapSmtpSyncService.swift
    16.6 -//  pEpForiOS
    16.7 -//
    16.8 -//  Created by Dirk Zimmermann on 29.06.17.
    16.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   16.10 -//
   16.11 -
   16.12 -import Foundation
   16.13 -
   16.14 -import MessageModel
   16.15 -
   16.16 -protocol ImapSmtpSyncServiceDelegate: class {
   16.17 -    /**
   16.18 -     Will be invoked once messages have been sent.
   16.19 -     - parameter service: The service that has sent the messages
   16.20 -     - parameter messages: The requested messages that have been sent
   16.21 -     - parameter allMessageIDs: The message IDs of *all* the messages that have been sent,
   16.22 -     requested or not.
   16.23 -     */
   16.24 -    func messagesSent(service: ImapSmtpSyncService,
   16.25 -                      messages: [Message],
   16.26 -                      allMessageIDs: [MessageID])
   16.27 -
   16.28 -    func handle(service: ImapSmtpSyncService, error: Error)
   16.29 -
   16.30 -    func didSync(service: ImapSmtpSyncService)
   16.31 -
   16.32 -    func startIdling(service: ImapSmtpSyncService)
   16.33 -
   16.34 -    /**
   16.35 -     Flags were uploaded to the server.
   16.36 -     */
   16.37 -    func flagsUploaded(message: Message)
   16.38 -}
   16.39 -
   16.40 -class ImapSmtpSyncService {
   16.41 -    weak var delegate: ImapSmtpSyncServiceDelegate?
   16.42 -
   16.43 -    let parentName: String
   16.44 -    let backgrounder: BackgroundTaskProtocol?
   16.45 -
   16.46 -    let serviceFactory = ServiceFactory()
   16.47 -    var currentlyRunningService: ServiceExecutionProtocol?
   16.48 -    var currentlyRunningIdleService: ImapIdleService?
   16.49 -
   16.50 -    var lastSuccessfullySentMessageIDs = [MessageID]()
   16.51 -
   16.52 -    enum State {
   16.53 -        case initial
   16.54 -        case initialSync
   16.55 -
   16.56 -        case sending
   16.57 -
   16.58 -        case readyForIdling
   16.59 -
   16.60 -        case enteringIdle
   16.61 -
   16.62 -        /** Using the real IMAP IDLE command */
   16.63 -        case idling
   16.64 -
   16.65 -        /** In case the server does not support IDLE */
   16.66 -        case waitingForNextSync
   16.67 -
   16.68 -        case reSyncing
   16.69 -
   16.70 -        case error
   16.71 -    }
   16.72 -
   16.73 -    private(set) var imapSyncData: ImapSyncData
   16.74 -    private(set) var smtpSendData: SmtpSendData
   16.75 -
   16.76 -    private var state: State = .initial
   16.77 -    private var sendRequested: Bool = false
   16.78 -    private var reSyncNecessary: Bool = false
   16.79 -    private var messagesEnqueuedForSend = [MessageID: Message]()
   16.80 -    private var messagesEnqueuedForFlagChange = Set<Message>()
   16.81 -    private var currentFolderName: String = ImapSync.defaultImapInboxName
   16.82 -
   16.83 -    var isReadyForImapAction: Bool {
   16.84 -        switch state {
   16.85 -        case .idling, .waitingForNextSync, .error, .readyForIdling:
   16.86 -            return true
   16.87 -        case .initial, .initialSync, .sending, .reSyncing, .enteringIdle:
   16.88 -            return false
   16.89 -        }
   16.90 -    }
   16.91 -
   16.92 -    var isIdling: Bool {
   16.93 -        switch state {
   16.94 -        case .idling, .waitingForNextSync:
   16.95 -            return true
   16.96 -        case .initial, .initialSync, .sending, .error, .readyForIdling, .reSyncing, .enteringIdle:
   16.97 -            return false
   16.98 -        }
   16.99 -    }
  16.100 -
  16.101 -    let workerQueue = DispatchQueue(
  16.102 -        label: "ImapSmtpSyncService", qos: .utility, target: nil)
  16.103 -
  16.104 -    init(parentName: String = #function, backgrounder: BackgroundTaskProtocol? = nil,
  16.105 -         imapSyncData: ImapSyncData, smtpSendData: SmtpSendData) {
  16.106 -        self.parentName = parentName
  16.107 -        self.backgrounder = backgrounder
  16.108 -        self.imapSyncData = imapSyncData
  16.109 -        self.smtpSendData = smtpSendData
  16.110 -    }
  16.111 -
  16.112 -    public func start() {
  16.113 -        workerQueue.async {
  16.114 -            inner()
  16.115 -        }
  16.116 -        func inner() {
  16.117 -            if state == .initial {
  16.118 -                state = .initialSync
  16.119 -                sendRequested = false
  16.120 -                let service = serviceFactory.initialSync(
  16.121 -                    parentName: parentName, backgrounder: backgrounder,
  16.122 -                    imapSyncData: imapSyncData, smtpSendData: smtpSendData,
  16.123 -                    smtpSendServiceDelegate: self,
  16.124 -                    syncFlagsToServerServiceDelegate: nil)
  16.125 -                currentlyRunningService = service
  16.126 -                service.execute() { [weak self] error in
  16.127 -                    self?.workerQueue.async {
  16.128 -                        self?.currentlyRunningService = nil
  16.129 -                        self?.handleInitialSyncFinished(error: error)
  16.130 -                    }
  16.131 -                }
  16.132 -            }
  16.133 -        }
  16.134 -    }
  16.135 -
  16.136 -    public func enqueueForSending(message: Message) {
  16.137 -        workerQueue.async {
  16.138 -            inner()
  16.139 -        }
  16.140 -        func inner() {
  16.141 -            let key = message.messageID
  16.142 -            messagesEnqueuedForSend[key] = message
  16.143 -            sendMessages()
  16.144 -        }
  16.145 -    }
  16.146 -
  16.147 -    public func enqueueForFlagChange(message: Message) {
  16.148 -        workerQueue.async {
  16.149 -            inner()
  16.150 -        }
  16.151 -        func inner() {
  16.152 -            messagesEnqueuedForFlagChange.insert(message)
  16.153 -            uploadFlagChanges(message: message)
  16.154 -        }
  16.155 -    }
  16.156 -
  16.157 -    func uploadFlagChanges(message: Message) {
  16.158 -        if isReadyForImapAction {
  16.159 -            cancelIdling()
  16.160 -            let folderName = message.parent.name
  16.161 -            let service = serviceFactory.syncFlagsToServer(
  16.162 -                parentName: parentName, backgrounder: backgrounder,
  16.163 -                imapSyncData: imapSyncData, folderName: folderName, syncFlagsDelegate: self)
  16.164 -
  16.165 -            currentlyRunningService = service
  16.166 -            service.execute() { [weak self] error in
  16.167 -                self?.workerQueue.async {
  16.168 -                    self?.currentlyRunningService = nil
  16.169 -                    self?.handleFlagUploadFinished(error: error)
  16.170 -                }
  16.171 -            }
  16.172 -        }
  16.173 -    }
  16.174 -
  16.175 -    public func cancel() {
  16.176 -        workerQueue.async {
  16.177 -            inner()
  16.178 -        }
  16.179 -        func inner() {
  16.180 -            currentlyRunningService?.cancel()
  16.181 -            imapSyncData.sync?.close()
  16.182 -            smtpSendData.smtp?.close()
  16.183 -        }
  16.184 -    }
  16.185 -
  16.186 -    func sendMessages()  {
  16.187 -        if isReadyForImapAction {
  16.188 -            cancelIdling()
  16.189 -            sendRequested = false
  16.190 -            state = .sending
  16.191 -            let service = SmtpSendService(
  16.192 -                parentName: parentName, backgrounder: backgrounder,
  16.193 -                imapSyncData: imapSyncData, smtpSendData: smtpSendData)
  16.194 -            currentlyRunningService = service
  16.195 -            service.execute() { [weak self] error in
  16.196 -                self?.workerQueue.async {
  16.197 -                    self?.currentlyRunningService = nil
  16.198 -                    self?.handleSendRequestFinished(error: error)
  16.199 -                }
  16.200 -            }
  16.201 -        } else {
  16.202 -            if state == .initial {
  16.203 -                start()
  16.204 -            } else {
  16.205 -                sendRequested = true
  16.206 -            }
  16.207 -        }
  16.208 -    }
  16.209 -
  16.210 -    func jumpIntoCorrectIdleState() {
  16.211 -        if state != .error {
  16.212 -            state = .readyForIdling
  16.213 -        }
  16.214 -    }
  16.215 -
  16.216 -    func handleError(error: Error?) {
  16.217 -        resetConnectionsOn(error: error)
  16.218 -        if let err = error {
  16.219 -            delegate?.handle(service: self, error: err)
  16.220 -            state = .error
  16.221 -        }
  16.222 -    }
  16.223 -
  16.224 -    func handleInitialSyncFinished(error: Error?) {
  16.225 -        handleError(error: error)
  16.226 -        notifyAboutSentMessages()
  16.227 -        if error == nil {
  16.228 -            delegate?.didSync(service: self)
  16.229 -            jumpIntoCorrectIdleState()
  16.230 -        }
  16.231 -        checkNextStep()
  16.232 -    }
  16.233 -
  16.234 -    func handleReSyncFinished(error: Error?) {
  16.235 -        handleError(error: error)
  16.236 -        if error == nil {
  16.237 -            delegate?.didSync(service: self)
  16.238 -            jumpIntoCorrectIdleState()
  16.239 -        }
  16.240 -        checkNextStep()
  16.241 -    }
  16.242 -
  16.243 -    func handleSendRequestFinished(error: Error?) {
  16.244 -        handleError(error: error)
  16.245 -        notifyAboutSentMessages()
  16.246 -        jumpIntoCorrectIdleState()
  16.247 -        checkNextStep()
  16.248 -    }
  16.249 -
  16.250 -    func handleFlagUploadFinished(error: Error?) {
  16.251 -        handleError(error: error)
  16.252 -        if error == nil {
  16.253 -            jumpIntoCorrectIdleState()
  16.254 -        }
  16.255 -        checkNextStep()
  16.256 -    }
  16.257 -
  16.258 -    func notifyAboutSentMessages() {
  16.259 -        var messagesWithSuccess = [Message]()
  16.260 -        for mID in lastSuccessfullySentMessageIDs {
  16.261 -            if let msg = messagesEnqueuedForSend[mID] {
  16.262 -                messagesWithSuccess.append(msg)
  16.263 -                messagesEnqueuedForSend[mID] = nil
  16.264 -            }
  16.265 -        }
  16.266 -
  16.267 -        if !messagesWithSuccess.isEmpty || !lastSuccessfullySentMessageIDs.isEmpty {
  16.268 -            delegate?.messagesSent(service: self, messages: messagesWithSuccess,
  16.269 -                                   allMessageIDs: lastSuccessfullySentMessageIDs)
  16.270 -        }
  16.271 -        lastSuccessfullySentMessageIDs.removeAll()
  16.272 -    }
  16.273 -    
  16.274 -    func resetConnectionsOn(error: Error?) {
  16.275 -        if let _ = error {
  16.276 -            imapSyncData.reset()
  16.277 -            smtpSendData.reset()
  16.278 -        }
  16.279 -    }
  16.280 -
  16.281 -    func reSync() {
  16.282 -        cancelIdling()
  16.283 -        let service = serviceFactory.reSync(
  16.284 -            parentName: parentName, backgrounder: backgrounder,
  16.285 -            imapSyncData: imapSyncData, folderName: currentFolderName)
  16.286 -        currentlyRunningService = service
  16.287 -        service.execute() { [weak self] error in
  16.288 -            self?.workerQueue.async {
  16.289 -                self?.currentlyRunningService = nil
  16.290 -                self?.handleReSyncFinished(error: error)
  16.291 -            }
  16.292 -        }
  16.293 -    }
  16.294 -
  16.295 -    func cancelIdling() {
  16.296 -        if isIdling {
  16.297 -            currentlyRunningIdleService?.cancel()
  16.298 -            currentlyRunningIdleService = nil
  16.299 -        }
  16.300 -    }
  16.301 -
  16.302 -    func checkNextStep() {
  16.303 -        if reSyncNecessary && isIdling {
  16.304 -            reSync()
  16.305 -            return
  16.306 -        }
  16.307 -        if sendRequested && isReadyForImapAction {
  16.308 -            sendMessages()
  16.309 -            return
  16.310 -        }
  16.311 -        if !messagesEnqueuedForFlagChange.isEmpty && isReadyForImapAction {
  16.312 -            if let msg = messagesEnqueuedForFlagChange.first {
  16.313 -                uploadFlagChanges(message: msg)
  16.314 -            }
  16.315 -        }
  16.316 -        if state == .readyForIdling {
  16.317 -            if imapSyncData.supportsIdle {
  16.318 -                state = .enteringIdle
  16.319 -                let imapIdleService = ImapIdleService(
  16.320 -                    parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData)
  16.321 -                imapIdleService.delegate = self
  16.322 -                currentlyRunningService = imapIdleService
  16.323 -                currentlyRunningIdleService = imapIdleService
  16.324 -                imapIdleService.execute() { [weak self] error in
  16.325 -                    self?.workerQueue.async {
  16.326 -                        self?.currentlyRunningService = nil
  16.327 -                        self?.handleError(error: error)
  16.328 -                        if error == nil {
  16.329 -                            switch imapIdleService.idleResult {
  16.330 -                            case .newMessages:
  16.331 -                                self?.reSyncNecessary = true
  16.332 -                                self?.checkNextStep()
  16.333 -                                break
  16.334 -                            case .error:
  16.335 -                                break
  16.336 -                            case .nothing:
  16.337 -                                break
  16.338 -                            case .idleExit:
  16.339 -                                break
  16.340 -                            }
  16.341 -                        }
  16.342 -                    }
  16.343 -                }
  16.344 -            } else {
  16.345 -                state = .waitingForNextSync
  16.346 -                delegate?.startIdling(service: self)
  16.347 -            }
  16.348 -            return
  16.349 -        }
  16.350 -    }
  16.351 -}
  16.352 -
  16.353 -extension ImapSmtpSyncService: SmtpSendServiceDelegate {
  16.354 -    func sent(messageIDs: [MessageID]) {
  16.355 -        lastSuccessfullySentMessageIDs.append(contentsOf: messageIDs)
  16.356 -    }
  16.357 -}
  16.358 -
  16.359 -extension ImapSmtpSyncService: SyncFlagsToServerServiceDelegate {
  16.360 -    func flagsUploaded(message: Message) {
  16.361 -        workerQueue.async {
  16.362 -            inner()
  16.363 -        }
  16.364 -        func inner() {
  16.365 -            if messagesEnqueuedForFlagChange.contains(message) {
  16.366 -                delegate?.flagsUploaded(message: message)
  16.367 -                messagesEnqueuedForFlagChange.remove(message)
  16.368 -            }
  16.369 -        }
  16.370 -    }
  16.371 -}
  16.372 -
  16.373 -extension ImapSmtpSyncService: ImapIdleServiceDelegate {
  16.374 -    func didEnterIdle(service: ImapIdleService) {
  16.375 -        state = .idling
  16.376 -        delegate?.startIdling(service: self)
  16.377 -    }
  16.378 -}
    17.1 --- a/pEpForiOS/Network/Service/MessageSyncService/MessageSyncService.swift	Wed Jul 04 11:24:51 2018 +0200
    17.2 +++ b/pEpForiOS/Network/Service/MessageSyncService/MessageSyncService.swift	Wed Jul 04 11:27:58 2018 +0200
    17.3 @@ -11,186 +11,18 @@
    17.4  
    17.5  import MessageModel
    17.6  
    17.7 -/**
    17.8 - ## IMAP sync operations
    17.9 - 
   17.10 - There are the following kind of IMAP syncs that should not run concurrent, but be serialized:
   17.11 - 
   17.12 - - *Standard IMAP sync*, which seems to be the same as the *initial sync*:
   17.13 -   - *Bail out* immediately if there is already that kind of sync running for that particual server.
   17.14 -   - If there are other IMAP operations running for that server, wait for them to finish.
   17.15 -   - Otherwise, just start:
   17.16 -     - Fetch *important folders*, that is:
   17.17 -       - Top level
   17.18 -       - The level below INBOX
   17.19 -     - Fetch newest Mails from INBOX
   17.20 - 
   17.21 - - IMAP operations for the same server, that are not directly sync related:
   17.22 -   - Delete/Append operations as consequence of *moving a message*.
   17.23 -     Since that changes the contents of folders, this should rightly block message fetch.
   17.24 -     An append operation should entail a subsequent fetch operation for that particular folder.
   17.25 -     A delete operations should be followed by a 'sync existing messages' operations.
   17.26 -   - *Flag change* operations. Also should block email fetch, and should entail the syncing
   17.27 -     of existing messages.
   17.28 -   - Append operations as a consequence of a *send message action* (see SMTP section).
   17.29 -     Should entail the fetch of new messages for that particular folder.
   17.30 -
   17.31 - Those can be synced by a wrapper around an operation queue, that also tracks
   17.32 - the *kind* of operations that are currently being executed.
   17.33 - 
   17.34 - Every unique IMAP server/login combination has its own queue object.
   17.35 - 
   17.36 - ## How to handle SMTP operations
   17.37 - 
   17.38 - In theory, they are independent of IMAP, and should not block the fetching of new emails.
   17.39 - In practice, every SMTP operation will be followed by some IMAP operations, like
   17.40 - append (to the sent folder). So an SMTP queue must have a reference to the corresponding
   17.41 - IMAP queue in order to request the pending IMAP actions.
   17.42 - 
   17.43 - ## User initiated actions
   17.44 - 
   17.45 - IDLE could mean the IMAP command, or polling periodically. The described actions
   17.46 - have to be executed for each account, or the account that is concerned.
   17.47 - 
   17.48 - This means that for every account, and certain folders, a state machine has to be executed.
   17.49 - 
   17.50 - - App start
   17.51 -   IMAP: fetch important folders, fetch new messages (INBOX), sync existing ones (INBOX),
   17.52 -   IDLE (INBOX).
   17.53 - - Send emails
   17.54 -   First SMTP, then IMAP: fetch new messages (sent folder), IDLE (INBOX).
   17.55 - - Visit a folder
   17.56 -   IMAP: fetch new messages (folder), sync existing ones (folder), IDLE (folder).
   17.57 - - Add new account
   17.58 -   Basically the same as for 'App start'.
   17.59 -
   17.60 - The fetching of important folders should happen periodically.
   17.61 - The fetching of new messages, and syncing of existing messages, for the INBOX,
   17.62 - and a currently visited folder, should happen periodically.
   17.63 - The INBOX should be handled by IDLE.
   17.64 - */
   17.65  class MessageSyncService: MessageSyncServiceProtocol {
   17.66 -    public enum InternalError: Error {
   17.67 -        case noAccount
   17.68 -        case noConnectInfos
   17.69 -    }
   17.70 -
   17.71 -    weak var errorDelegate: MessageSyncServiceErrorDelegate?
   17.72 -    weak var sentDelegate: MessageSyncServiceSentDelegate?
   17.73 -    weak var syncDelegate: MessageSyncServiceSyncDelegate?
   17.74 -    weak var stateDelegate: MessageSyncServiceStateDelegate?
   17.75 -    weak var flagsUploadDelegate: MessageSyncFlagsUploadDelegate?
   17.76 -
   17.77 -    let sleepTimeInSeconds: Double
   17.78      let parentName: String
   17.79 -    let backgrounder: BackgroundTaskProtocol?
   17.80 -    let mySelfer: KickOffMySelfProtocol?
   17.81 -    let managementQueue = DispatchQueue(
   17.82 -        label: "MessageSyncService", qos: .utility, target: nil)
   17.83 -
   17.84 -    private var imapConnections = [ImapSmtpConnection: ImapSmtpSyncService]()
   17.85 -
   17.86 -    var accountVerifications = [Account:
   17.87 -        (AccountVerificationService, AccountVerificationServiceDelegate)]()
   17.88 +    let managementQueue = DispatchQueue(label: "pep.security.MessageSyncService.managementQueue",
   17.89 +                                        qos: .utility,
   17.90 +                                        target: nil)
   17.91 +    var accountVerifications =
   17.92 +        [Account:(AccountVerificationService, AccountVerificationServiceDelegate)]()
   17.93  
   17.94      let fetchOlderImapMessagesService = FetchOlderImapMessagesService()
   17.95  
   17.96 -    init(sleepTimeInSeconds: Double = 10.0,
   17.97 -         parentName: String = #function, backgrounder: BackgroundTaskProtocol? = nil,
   17.98 -         mySelfer: KickOffMySelfProtocol? = nil) {
   17.99 -        self.sleepTimeInSeconds = sleepTimeInSeconds
  17.100 +    init(parentName: String = #function) {
  17.101          self.parentName = parentName
  17.102 -        self.backgrounder = backgrounder
  17.103 -        self.mySelfer = mySelfer
  17.104 -    }
  17.105 -
  17.106 -    func start(account: Account) {
  17.107 -        connectInfos(account: account) { [weak self] (ici, sci) in
  17.108 -            self?.managementQueue.async {
  17.109 -                self?.startInternal(imapConnectInfo: ici,
  17.110 -                                    smtpConnectInfo: sci)
  17.111 -            }
  17.112 -        }
  17.113 -    }
  17.114 -
  17.115 -    func requestSend(message: Message) {
  17.116 -        connectInfos(account: message.parent.account) { [weak self] (imapCI, smtpCI) in
  17.117 -            self?.managementQueue.async { [weak self] in
  17.118 -                self?.handleSendRequest(imapConnectInfo: imapCI,
  17.119 -                                        smtpConnectInfo: smtpCI, message: message)
  17.120 -            }
  17.121 -        }
  17.122 -    }
  17.123 -
  17.124 -    func requestDraft(message: Message) {
  17.125 -        Log.shared.errorAndCrash(component: #function, errorString: "not implemented")
  17.126 -    }
  17.127 -
  17.128 -    func requestMessageSync(folder: Folder) {
  17.129 -        Log.shared.errorAndCrash(component: #function, errorString: "not implemented")
  17.130 -    }
  17.131 -
  17.132 -    func requestFlagChange(message: Message) {
  17.133 -        connectInfos(account: message.parent.account) { [weak self] (imapCI, smtpCI) in
  17.134 -            self?.managementQueue.async { [weak self] in
  17.135 -                self?.handleFlagChange(imapConnectInfo: imapCI,
  17.136 -                                       smtpConnectInfo: smtpCI, message: message)
  17.137 -            }
  17.138 -        }
  17.139 -    }
  17.140 -
  17.141 -    func cancel(account: Account) {
  17.142 -        connectInfos(account: account) { [weak self] (ici, sci) in
  17.143 -            self?.managementQueue.async {
  17.144 -                self?.cancelInternal(imapConnectInfo: ici,
  17.145 -                                     smtpConnectInfo: sci)
  17.146 -            }
  17.147 -        }
  17.148 -    }
  17.149 -
  17.150 -    /**
  17.151 -     Cancel all accounts.
  17.152 -     */
  17.153 -    func cancel() {
  17.154 -        self.managementQueue.async {
  17.155 -            for (_, v) in self.imapConnections {
  17.156 -                v.cancel()
  17.157 -            }
  17.158 -        }
  17.159 -    }
  17.160 -
  17.161 -    private func indicate(error: Error) {
  17.162 -        managementQueue.async { [weak self] in
  17.163 -            self?.errorDelegate?.show(error: error)
  17.164 -        }
  17.165 -    }
  17.166 -
  17.167 -    private func lookUpOrCreateImapSmtpService(
  17.168 -        imapConnectInfo: EmailConnectInfo,
  17.169 -        smtpConnectInfo: EmailConnectInfo) -> ImapSmtpSyncService {
  17.170 -        let key = ImapSmtpConnection(
  17.171 -            imapConnectInfo: imapConnectInfo, smtpConnectInfo: smtpConnectInfo)
  17.172 -        let model = imapConnections[key] ??
  17.173 -            ImapSmtpSyncService(
  17.174 -                parentName: parentName,
  17.175 -                backgrounder: backgrounder,
  17.176 -                imapSyncData: ImapSyncData(connectInfo: imapConnectInfo),
  17.177 -                smtpSendData: SmtpSendData(connectInfo: smtpConnectInfo))
  17.178 -        model.delegate = self
  17.179 -        imapConnections[key] = model
  17.180 -        return model
  17.181 -    }
  17.182 -
  17.183 -    private func startInternal(imapConnectInfo: EmailConnectInfo,
  17.184 -                               smtpConnectInfo: EmailConnectInfo) {
  17.185 -        lookUpOrCreateImapSmtpService(
  17.186 -            imapConnectInfo: imapConnectInfo, smtpConnectInfo: smtpConnectInfo).start()
  17.187 -    }
  17.188 -    
  17.189 -    private func cancelInternal(imapConnectInfo: EmailConnectInfo,
  17.190 -                                smtpConnectInfo: EmailConnectInfo) {
  17.191 -        lookUpOrCreateImapSmtpService(
  17.192 -            imapConnectInfo: imapConnectInfo, smtpConnectInfo: smtpConnectInfo).cancel()
  17.193      }
  17.194  
  17.195      private func requestVerificationInternal(account: Account,
  17.196 @@ -204,52 +36,17 @@
  17.197      private func requestFetchOlderImapMessagesInternal(forFolder folder: Folder) {
  17.198          fetchOlderImapMessagesService.fetchOlderMessages(inFolder: folder)
  17.199      }
  17.200 -
  17.201 -    private func handleSendRequest(imapConnectInfo: EmailConnectInfo,
  17.202 -                           smtpConnectInfo: EmailConnectInfo, message: Message) {
  17.203 -        lookUpOrCreateImapSmtpService(
  17.204 -            imapConnectInfo: imapConnectInfo,
  17.205 -            smtpConnectInfo: smtpConnectInfo).enqueueForSending(message: message)
  17.206 -    }
  17.207 -
  17.208 -    private func handleFlagChange(imapConnectInfo: EmailConnectInfo,
  17.209 -                                  smtpConnectInfo: EmailConnectInfo, message: Message) {
  17.210 -        lookUpOrCreateImapSmtpService(
  17.211 -            imapConnectInfo: imapConnectInfo,
  17.212 -            smtpConnectInfo: smtpConnectInfo).enqueueForFlagChange(message: message)
  17.213 -    }
  17.214 -
  17.215 -    private func connectInfos(
  17.216 -        account: Account?,
  17.217 -        context: NSManagedObjectContext) -> (EmailConnectInfo, EmailConnectInfo)? {
  17.218 -        if let theAccount = account,
  17.219 -            let cdAccount = CdAccount.search(account: theAccount, context: context),
  17.220 -            let imapCI = cdAccount.imapConnectInfo, let smtpCI = cdAccount.smtpConnectInfo {
  17.221 -            return (imapCI, smtpCI)
  17.222 -        }
  17.223 -        return nil
  17.224 -    }
  17.225 -
  17.226 -    private func connectInfos(account: Account?,
  17.227 -                              handler: @escaping (EmailConnectInfo, EmailConnectInfo) -> ()) {
  17.228 -        guard let theAccount = account else {
  17.229 -            indicate(error: InternalError.noAccount)
  17.230 -            return
  17.231 -        }
  17.232 -        let context = Record.Context.background
  17.233 -        context.perform { [weak self] in
  17.234 -            if let (iCI, sCI) = self?.connectInfos(account: theAccount, context: context) {
  17.235 -                handler(iCI, sCI)
  17.236 -            } else {
  17.237 -                self?.indicate(error: InternalError.noConnectInfos)
  17.238 -            }
  17.239 -        }
  17.240 -    }
  17.241  }
  17.242  
  17.243  // MARK: - MessageSyncServiceProtocol
  17.244  
  17.245  extension MessageSyncService {
  17.246 +
  17.247 +    /**
  17.248 +     Request account verification, receiving news via the delegate.
  17.249 +     Backend might start syncing the inbox as soon as the verification
  17.250 +     was successful.
  17.251 +     */
  17.252      func requestVerification(account: Account, delegate: AccountVerificationServiceDelegate) {
  17.253          managementQueue.async {
  17.254              self.requestVerificationInternal(account: account, delegate: delegate)
  17.255 @@ -281,41 +78,3 @@
  17.256          }
  17.257      }
  17.258  }
  17.259 -
  17.260 -// MARK: - ImapSmtpSyncServiceDelegate
  17.261 -
  17.262 -extension MessageSyncService: ImapSmtpSyncServiceDelegate {
  17.263 -    func messagesSent(service: ImapSmtpSyncService, messages: [Message],
  17.264 -                      allMessageIDs: [MessageID]) {
  17.265 -        for msg in messages {
  17.266 -            sentDelegate?.didSend(message: msg)
  17.267 -        }
  17.268 -        sentDelegate?.didSend(messageIDs: allMessageIDs)
  17.269 -    }
  17.270 -
  17.271 -    func handle(service: ImapSmtpSyncService, error: Error) {
  17.272 -        errorDelegate?.show(error: error)
  17.273 -    }
  17.274 -
  17.275 -    func didSync(service: ImapSmtpSyncService) {
  17.276 -        if syncDelegate == nil {
  17.277 -            return
  17.278 -        }
  17.279 -        service.imapSyncData.connectInfo.handleAccount() { [weak self] account in
  17.280 -            self?.syncDelegate?.didSync(account: account)
  17.281 -        }
  17.282 -    }
  17.283 -
  17.284 -    func startIdling(service: ImapSmtpSyncService) {
  17.285 -        if stateDelegate == nil {
  17.286 -            return
  17.287 -        }
  17.288 -        service.imapSyncData.connectInfo.handleAccount() { [weak self] account in
  17.289 -            self?.stateDelegate?.startIdling(account: account)
  17.290 -        }
  17.291 -    }
  17.292 -
  17.293 -    func flagsUploaded(message: Message) {
  17.294 -        flagsUploadDelegate?.flagsUploaded(message: message)
  17.295 -    }
  17.296 -}
    18.1 --- a/pEpForiOS/Network/Service/MessageSyncService/MessageSyncServiceProtocol.swift	Wed Jul 04 11:24:51 2018 +0200
    18.2 +++ b/pEpForiOS/Network/Service/MessageSyncService/MessageSyncServiceProtocol.swift	Wed Jul 04 11:27:58 2018 +0200
    18.3 @@ -7,56 +7,8 @@
    18.4  //
    18.5  
    18.6  import Foundation
    18.7 -
    18.8  import MessageModel
    18.9  
   18.10 -protocol MessageSyncServiceFolderDelegate {
   18.11 -    /**
   18.12 -     The folders were fetched for the indicated account.
   18.13 -     */
   18.14 -    func didFetchFolders(forAccount: Account)
   18.15 -}
   18.16 -
   18.17 -protocol MessageSyncServiceSentDelegate: class {
   18.18 -    /**
   18.19 -     The indicated message has been sent, as requested.
   18.20 -     */
   18.21 -    func didSend(message: Message)
   18.22 -
   18.23 -    /**
   18.24 -     The indicated message IDs have been sent, regardless whether requested or not.
   18.25 -     */
   18.26 -    func didSend(messageIDs: [MessageID])
   18.27 -}
   18.28 -
   18.29 -protocol MessageSyncServiceErrorDelegate: class {
   18.30 -    /**
   18.31 -     An error occurred, and should usually displayed.
   18.32 -     */
   18.33 -    func show(error: Error)
   18.34 -}
   18.35 -
   18.36 -protocol MessageSyncServiceSyncDelegate: class {
   18.37 -    /**
   18.38 -     Called when the given account just got synced, and the service will enter idling.
   18.39 -     */
   18.40 -    func didSync(account: Account)
   18.41 -}
   18.42 -
   18.43 -protocol MessageSyncServiceStateDelegate: class {
   18.44 -    /**
   18.45 -     Called when the state reaches the idling (or polling) phase.
   18.46 -     */
   18.47 -    func startIdling(account: Account)
   18.48 -}
   18.49 -
   18.50 -protocol MessageSyncFlagsUploadDelegate: class {
   18.51 -    /**
   18.52 -     Called when local flags have been updated on the server.
   18.53 -     */
   18.54 -    func flagsUploaded(message: Message)
   18.55 -}
   18.56 -
   18.57  /**
   18.58   Message sync related actions that can be requested by the UI.
   18.59   The purpose is to make network-related actions seem as fast as possible,
   18.60 @@ -64,12 +16,6 @@
   18.61   have full control over the scheduling.
   18.62   */
   18.63  protocol MessageSyncServiceProtocol {
   18.64 -    var errorDelegate: MessageSyncServiceErrorDelegate? { get set }
   18.65 -    var sentDelegate: MessageSyncServiceSentDelegate? { get set }
   18.66 -    var syncDelegate: MessageSyncServiceSyncDelegate? { get set }
   18.67 -    var stateDelegate: MessageSyncServiceStateDelegate? { get set }
   18.68 -    var flagsUploadDelegate: MessageSyncFlagsUploadDelegate? { get set }
   18.69 -
   18.70      /**
   18.71       Request account verification, receiving news via the delegate.
   18.72       Backend might start syncing the inbox as soon as the verification
   18.73 @@ -78,37 +24,4 @@
   18.74      func requestVerification(account: Account, delegate: AccountVerificationServiceDelegate)
   18.75  
   18.76      func requestFetchOlderMessages(inFolder folder: Folder)
   18.77 -
   18.78 -    /**
   18.79 -     Requests the given message to be drafted.
   18.80 -     */
   18.81 -    func requestDraft(message: Message)
   18.82 -
   18.83 -    /**
   18.84 -     Notify the backend that the user just finished creating a message for sending,
   18.85 -     which should be sent out as fast as possible.
   18.86 -     */
   18.87 -    func requestSend(message: Message)
   18.88 -
   18.89 -    /**
   18.90 -     Notify the backend that the user changed flags for the given message.
   18.91 -     The changes should be propagated as fast as possible to the server.
   18.92 -     */
   18.93 -    func requestFlagChange(message: Message)
   18.94 -
   18.95 -    /**
   18.96 -     Message changes will be delivered via the `MessageFolderDelegate` mechanism.
   18.97 -     Backend may sync the inbox even if the UI never requested it.
   18.98 -     */
   18.99 -    func requestMessageSync(folder: Folder)
  18.100 -
  18.101 -    /**
  18.102 -     Starts syncing folders, INBOX etc.
  18.103 -     */
  18.104 -    func start(account: Account)
  18.105 -
  18.106 -    /**
  18.107 -     Cancels syncing the given account.
  18.108 -     */
  18.109 -    func cancel(account: Account)
  18.110  }
    19.1 --- a/pEpForiOS/Network/Service/MessageSyncService/ServiceChainExecutor.swift	Wed Jul 04 11:24:51 2018 +0200
    19.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.3 @@ -1,63 +0,0 @@
    19.4 -//
    19.5 -//  ServiceChainExecutor.swift
    19.6 -//  pEpForiOS
    19.7 -//
    19.8 -//  Created by Dirk Zimmermann on 07.07.17.
    19.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   19.10 -//
   19.11 -
   19.12 -import Foundation
   19.13 -
   19.14 -class ServiceChainExecutor {
   19.15 -    var services = [ServiceExecutionProtocol]()
   19.16 -    var currentlyExecutingService: ServiceExecutionProtocol?
   19.17 -
   19.18 -    init(services: [ServiceExecutionProtocol]) {
   19.19 -        self.services = services
   19.20 -    }
   19.21 -
   19.22 -    convenience init() {
   19.23 -        self.init(services: [])
   19.24 -    }
   19.25 -
   19.26 -    func add(service: ServiceExecutionProtocol) {
   19.27 -        services.append(service)
   19.28 -    }
   19.29 -
   19.30 -    func add(services: [ServiceExecutionProtocol]) {
   19.31 -        for s in services {
   19.32 -            add(service: s)
   19.33 -        }
   19.34 -    }
   19.35 -}
   19.36 -
   19.37 -extension ServiceChainExecutor: ServiceExecutionProtocol {
   19.38 -    func cancel() {
   19.39 -        for service in services {
   19.40 -            service.cancel()
   19.41 -        }
   19.42 -    }
   19.43 -
   19.44 -    func execute(handler: ServiceFinishedHandler? = nil) {
   19.45 -        if let service = services.first {
   19.46 -            let _ = services.remove(at: 0)
   19.47 -            Log.shared.info(component: #function, content: "executing \(service)")
   19.48 -            currentlyExecutingService = service
   19.49 -            service.execute() { [weak self] error in
   19.50 -                if let err = error {
   19.51 -                    let desc = String(describing: self?.currentlyExecutingService)
   19.52 -                    Log.shared.error(
   19.53 -                        component: #function,
   19.54 -                        errorString: "Error for \(desc): ",
   19.55 -                        error: err)
   19.56 -                    handler?(err)
   19.57 -                } else {
   19.58 -                    self?.execute(handler: handler)
   19.59 -                }
   19.60 -            }
   19.61 -        } else {
   19.62 -            currentlyExecutingService = nil
   19.63 -            handler?(nil)
   19.64 -        }
   19.65 -    }
   19.66 -}
    20.1 --- a/pEpForiOS/Network/Service/MessageSyncService/ServiceFactory.swift	Wed Jul 04 11:24:51 2018 +0200
    20.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.3 @@ -1,71 +0,0 @@
    20.4 -//
    20.5 -//  ServiceFactory.swift
    20.6 -//  pEpForiOS
    20.7 -//
    20.8 -//  Created by Dirk Zimmermann on 10.07.17.
    20.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   20.10 -//
   20.11 -
   20.12 -import Foundation
   20.13 -
   20.14 -import MessageModel
   20.15 -
   20.16 -class ServiceFactory {
   20.17 -    func initialSync(
   20.18 -        parentName: String, backgrounder: BackgroundTaskProtocol?,
   20.19 -        imapSyncData: ImapSyncData, smtpSendData: SmtpSendData,
   20.20 -        smtpSendServiceDelegate: SmtpSendServiceDelegate?,
   20.21 -        syncFlagsToServerServiceDelegate: SyncFlagsToServerServiceDelegate?) -> ServiceExecutionProtocol {
   20.22 -        let syncFoldersService = SyncFoldersFromServerService(
   20.23 -            parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData)
   20.24 -
   20.25 -        let smtpService = SmtpSendService(
   20.26 -            parentName: parentName, backgrounder: backgrounder,
   20.27 -            imapSyncData: imapSyncData, smtpSendData: smtpSendData)
   20.28 -        smtpService.delegate = smtpSendServiceDelegate
   20.29 -
   20.30 -        let fetchMessagesService = FetchMessagesService(
   20.31 -            parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData)
   20.32 -
   20.33 -        let syncMessagesService = SyncExistingMessagesService(
   20.34 -            parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData)
   20.35 -
   20.36 -        let uploadFlagsService = SyncFlagsToServerService(
   20.37 -            parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData,
   20.38 -            folderName: ImapSync.defaultImapInboxName)
   20.39 -        uploadFlagsService.delegate = syncFlagsToServerServiceDelegate
   20.40 -
   20.41 -        let chainedService = ServiceChainExecutor()
   20.42 -        chainedService.add(services: [syncFoldersService, smtpService,
   20.43 -                                      fetchMessagesService, syncMessagesService,
   20.44 -                                      uploadFlagsService])
   20.45 -
   20.46 -        return chainedService
   20.47 -    }
   20.48 -
   20.49 -    func reSync(
   20.50 -        parentName: String, backgrounder: BackgroundTaskProtocol?,
   20.51 -        imapSyncData: ImapSyncData, folderName: String) -> ServiceExecutionProtocol {
   20.52 -        let fetchMessagesService = FetchMessagesService(
   20.53 -            parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData)
   20.54 -
   20.55 -        let syncMessagesService = SyncExistingMessagesService(
   20.56 -            parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData)
   20.57 -
   20.58 -        let chainedService = ServiceChainExecutor()
   20.59 -        chainedService.add(services: [fetchMessagesService, syncMessagesService])
   20.60 -
   20.61 -        return chainedService
   20.62 -    }
   20.63 -
   20.64 -    func syncFlagsToServer(
   20.65 -        parentName: String, backgrounder: BackgroundTaskProtocol?,
   20.66 -        imapSyncData: ImapSyncData, folderName: String,
   20.67 -        syncFlagsDelegate: SyncFlagsToServerServiceDelegate?) -> ServiceExecutionProtocol {
   20.68 -        let uploadFlagsService = SyncFlagsToServerService(
   20.69 -            parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData,
   20.70 -            folderName: folderName)
   20.71 -        uploadFlagsService.delegate = syncFlagsDelegate
   20.72 -        return uploadFlagsService
   20.73 -    }
   20.74 -}
    21.1 --- a/pEpForiOS/Network/Service/MessageSyncService/SmtpSendService.swift	Wed Jul 04 11:24:51 2018 +0200
    21.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.3 @@ -1,93 +0,0 @@
    21.4 -//
    21.5 -//  SmtpSendService.swift
    21.6 -//  pEpForiOS
    21.7 -//
    21.8 -//  Created by Dirk Zimmermann on 27.06.17.
    21.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   21.10 -//
   21.11 -
   21.12 -import Foundation
   21.13 -
   21.14 -import MessageModel
   21.15 -
   21.16 -protocol SmtpSendServiceDelegate: class {
   21.17 -    func sent(messageIDs: [MessageID])
   21.18 -}
   21.19 -
   21.20 -class SmtpSendService: BackgroundOperationImapService {
   21.21 -    weak var delegate: SmtpSendServiceDelegate?
   21.22 -
   21.23 -    private let smtpSendData: SmtpSendData
   21.24 -
   21.25 -    init(parentName: String, backgrounder: BackgroundTaskProtocol? = nil,
   21.26 -         imapSyncData: ImapSyncData, smtpSendData: SmtpSendData) {
   21.27 -        self.smtpSendData = smtpSendData
   21.28 -        super.init(parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData)
   21.29 -    }
   21.30 -
   21.31 -    private func haveMailsToEncrypt(smtpSendData: SmtpSendData,
   21.32 -                                        imapSyncData: ImapSyncData,
   21.33 -                                        bgID: BackgroundTaskID?,
   21.34 -                                        handler: ServiceFinishedHandler? = nil) {
   21.35 -        let smtpLoginOp = LoginSmtpOperation(
   21.36 -            parentName: parentName, smtpSendData: smtpSendData, errorContainer: self)
   21.37 -        let sendOp = EncryptAndSendOperation(
   21.38 -            parentName: parentName, smtpSendData: smtpSendData, errorContainer: self)
   21.39 -        sendOp.addDependency(smtpLoginOp)
   21.40 -
   21.41 -        let imapLoginOp = LoginImapOperation(
   21.42 -            parentName: parentName, errorContainer: self, imapSyncData: imapSyncData)
   21.43 -        imapLoginOp.addDependency(sendOp)
   21.44 -        let appendOp = AppendSendMailsOperation(
   21.45 -            parentName: parentName, imapSyncData: imapSyncData, errorContainer: self)
   21.46 -        appendOp.addDependency(imapLoginOp)
   21.47 -        appendOp.completionBlock = { [weak self] in
   21.48 -            appendOp.completionBlock = nil
   21.49 -            self?.delegate?.sent(messageIDs: appendOp.successAppendedMessageIDs)
   21.50 -            handler?(self?.error)
   21.51 -            self?.backgrounder?.endBackgroundTask(bgID)
   21.52 -        }
   21.53 -
   21.54 -        backgroundQueue.addOperations(
   21.55 -            [smtpLoginOp, sendOp, imapLoginOp, appendOp], waitUntilFinished: false)
   21.56 -    }
   21.57 -
   21.58 -    override func cancel() {
   21.59 -        super.cancel()
   21.60 -        smtpSendData.smtp?.delegate = nil
   21.61 -    }
   21.62 -}
   21.63 -
   21.64 -extension SmtpSendService: ServiceExecutionProtocol {
   21.65 -    func execute(handler: ServiceFinishedHandler? = nil) {
   21.66 -        let bgID = backgrounder?.beginBackgroundTask(taskName: "SmtpSendService")
   21.67 -        let context = Record.Context.background
   21.68 -        context.perform { [weak self] in
   21.69 -            guard
   21.70 -                let imapSyncData = self?.imapSyncData,
   21.71 -                let smtpSendData = self?.smtpSendData else {
   21.72 -                    self?.handle(
   21.73 -                        error: BackgroundError.GeneralError.invalidParameter(info: #function),
   21.74 -                        taskID: bgID,
   21.75 -                        handler: handler)
   21.76 -                    return
   21.77 -            }
   21.78 -
   21.79 -            guard let cdAccount = context.object(with: smtpSendData.connectInfo.accountObjectID)
   21.80 -                as? CdAccount else {
   21.81 -                    handler?(BackgroundError.CoreDataError.couldNotFindAccount(info: nil))
   21.82 -                    self?.backgrounder?.endBackgroundTask(bgID)
   21.83 -                    return
   21.84 -            }
   21.85 -            if let _ = EncryptAndSendOperation.retrieveNextMessage(
   21.86 -                context: context, cdAccount: cdAccount) {
   21.87 -                self?.haveMailsToEncrypt(
   21.88 -                    smtpSendData: smtpSendData, imapSyncData: imapSyncData,
   21.89 -                    bgID: bgID, handler: handler)
   21.90 -            } else {
   21.91 -                handler?(nil)
   21.92 -                self?.backgrounder?.endBackgroundTask(bgID)
   21.93 -            }
   21.94 -        }
   21.95 -    }
   21.96 -}
    22.1 --- a/pEpForiOS/Network/Service/MessageSyncService/SyncExistingMessagesService.swift	Wed Jul 04 11:24:51 2018 +0200
    22.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.3 @@ -1,63 +0,0 @@
    22.4 -//
    22.5 -//  SyncExistingMessagesService.swift
    22.6 -//  pEpForiOS
    22.7 -//
    22.8 -//  Created by Dirk Zimmermann on 06.07.17.
    22.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   22.10 -//
   22.11 -
   22.12 -import Foundation
   22.13 -import CoreData
   22.14 -
   22.15 -import MessageModel
   22.16 -
   22.17 -class SyncExistingMessagesService: BackgroundOperationImapService {
   22.18 -    let folderName: String
   22.19 -
   22.20 -    init(parentName: String = #function, backgrounder: BackgroundTaskProtocol? = nil,
   22.21 -         imapSyncData: ImapSyncData,
   22.22 -         folderName: String = ImapSync.defaultImapInboxName) {
   22.23 -        self.folderName = folderName
   22.24 -        super.init(parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData)
   22.25 -    }
   22.26 -
   22.27 -    func executeInternal(
   22.28 -        context: NSManagedObjectContext, taskID: BackgroundTaskID?,
   22.29 -        handler: ServiceFinishedHandler?) {
   22.30 -        do {
   22.31 -            let cdFolder = try imapSyncData.connectInfo.folderBy(name: folderName, context: context)
   22.32 -            let loginOp = LoginImapOperation(
   22.33 -                parentName: parentName, errorContainer: self, imapSyncData: imapSyncData)
   22.34 -
   22.35 -            guard let syncOp = SyncMessagesOperation(
   22.36 -                parentName: parentName, errorContainer: self, imapSyncData: imapSyncData,
   22.37 -                folder: cdFolder) else {
   22.38 -                    handle(error: BackgroundError.GeneralError.invalidParameter(info: #function),
   22.39 -                           taskID: taskID,
   22.40 -                           handler: handler)
   22.41 -                    return
   22.42 -            }
   22.43 -
   22.44 -            syncOp.addDependency(loginOp)
   22.45 -            syncOp.completionBlock = {  [weak self] in
   22.46 -                syncOp.completionBlock = nil
   22.47 -                self?.backgrounder?.endBackgroundTask(taskID)
   22.48 -                handler?(self?.error)
   22.49 -            }
   22.50 -
   22.51 -            backgroundQueue.addOperations([loginOp, syncOp], waitUntilFinished: false)
   22.52 -        } catch let err {
   22.53 -            handle(error: err, taskID: taskID, handler: handler)
   22.54 -        }
   22.55 -    }
   22.56 -}
   22.57 -
   22.58 -extension SyncExistingMessagesService: ServiceExecutionProtocol {
   22.59 -    func execute(handler: ServiceFinishedHandler? = nil) {
   22.60 -        let bgID = backgrounder?.beginBackgroundTask(taskName: "SyncExistingMessagesService")
   22.61 -        let context = Record.Context.background
   22.62 -        context.perform { [weak self] in
   22.63 -            self?.executeInternal(context: context, taskID: bgID, handler: handler)
   22.64 -        }
   22.65 -    }
   22.66 -}
    23.1 --- a/pEpForiOS/Network/Service/MessageSyncService/SyncFlagsToServerService.swift	Wed Jul 04 11:24:51 2018 +0200
    23.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.3 @@ -1,77 +0,0 @@
    23.4 -//
    23.5 -//  SyncFlagsToServerService.swift
    23.6 -//  pEpForiOS
    23.7 -//
    23.8 -//  Created by Dirk Zimmermann on 24.07.17.
    23.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   23.10 -//
   23.11 -
   23.12 -import Foundation
   23.13 -import CoreData
   23.14 -
   23.15 -import MessageModel
   23.16 -
   23.17 -protocol SyncFlagsToServerServiceDelegate: class {
   23.18 -    func flagsUploaded(message: Message)
   23.19 -}
   23.20 -
   23.21 -class SyncFlagsToServerService: BackgroundOperationImapService {
   23.22 -    let folderName: String
   23.23 -    weak var delegate: SyncFlagsToServerServiceDelegate?
   23.24 -
   23.25 -    init(parentName: String = #function, backgrounder: BackgroundTaskProtocol? = nil,
   23.26 -         imapSyncData: ImapSyncData,
   23.27 -         folderName: String = ImapSync.defaultImapInboxName) {
   23.28 -        self.folderName = folderName
   23.29 -        super.init(parentName: parentName, backgrounder: backgrounder, imapSyncData: imapSyncData)
   23.30 -    }
   23.31 -
   23.32 -    func executeInternal(
   23.33 -        context: NSManagedObjectContext, taskID: BackgroundTaskID?,
   23.34 -        handler: ServiceFinishedHandler?) {
   23.35 -        do {
   23.36 -            let cdFolder = try imapSyncData.connectInfo.folderBy(name: folderName, context: context)
   23.37 -            let loginOp = LoginImapOperation(
   23.38 -                parentName: parentName, errorContainer: self, imapSyncData: imapSyncData)
   23.39 -
   23.40 -            guard let syncOp = SyncFlagsToServerOperation(
   23.41 -                parentName: parentName, errorContainer: self, imapSyncData: imapSyncData,
   23.42 -                folder: cdFolder) else {
   23.43 -                    handle(error: BackgroundError.GeneralError.invalidParameter(info: #function),
   23.44 -                           taskID: taskID,
   23.45 -                           handler: handler)
   23.46 -                    return
   23.47 -            }
   23.48 -
   23.49 -            syncOp.addDependency(loginOp)
   23.50 -            syncOp.delegate = self
   23.51 -            syncOp.completionBlock = {  [weak self] in
   23.52 -                syncOp.completionBlock = nil
   23.53 -                self?.backgrounder?.endBackgroundTask(taskID)
   23.54 -                handler?(self?.error)
   23.55 -            }
   23.56 -
   23.57 -            backgroundQueue.addOperations([loginOp, syncOp], waitUntilFinished: false)
   23.58 -        } catch let err {
   23.59 -            handle(error: err, taskID: taskID, handler: handler)
   23.60 -        }
   23.61 -    }
   23.62 -}
   23.63 -
   23.64 -extension SyncFlagsToServerService: ServiceExecutionProtocol {
   23.65 -    func execute(handler: ServiceFinishedHandler? = nil) {
   23.66 -        let bgID = backgrounder?.beginBackgroundTask(taskName: "SyncFlagsToServerService")
   23.67 -        let context = Record.Context.background
   23.68 -        context.perform { [weak self] in
   23.69 -            self?.executeInternal(context: context, taskID: bgID, handler: handler)
   23.70 -        }
   23.71 -    }
   23.72 -}
   23.73 -
   23.74 -extension SyncFlagsToServerService: SyncFlagsToServerOperationDelegate {
   23.75 -    func flagsUploaded(cdMessage: CdMessage) {
   23.76 -        if let del = delegate, let msg = cdMessage.message() {
   23.77 -            del.flagsUploaded(message: msg)
   23.78 -        }
   23.79 -    }
   23.80 -}
    24.1 --- a/pEpForiOS/Network/Service/MessageSyncService/SyncFoldersFromServerService.swift	Wed Jul 04 11:24:51 2018 +0200
    24.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.3 @@ -1,47 +0,0 @@
    24.4 -//
    24.5 -//  SyncFoldersFromServerService.swift
    24.6 -//  pEpForiOS
    24.7 -//
    24.8 -//  Created by Dirk Zimmermann on 03.07.17.
    24.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   24.10 -//
   24.11 -
   24.12 -import Foundation
   24.13 -
   24.14 -import MessageModel
   24.15 -
   24.16 -protocol SyncFoldersFromServerServiceDelegate: class {
   24.17 -    /**
   24.18 -     Called if the fetched folder was unknown before, and therefore newly created.
   24.19 -     */
   24.20 -    func didCreate(folder: Folder)
   24.21 -}
   24.22 -
   24.23 -class SyncFoldersFromServerService: BackgroundOperationImapService {
   24.24 -    weak var delegate: SyncFoldersFromServerServiceDelegate?
   24.25 -}
   24.26 -
   24.27 -extension SyncFoldersFromServerService: SyncFoldersFromServerOperationDelegate {
   24.28 -    func didCreate(cdFolder: CdFolder) {
   24.29 -        delegate?.didCreate(folder: cdFolder.folder())
   24.30 -    }
   24.31 -}
   24.32 -
   24.33 -extension SyncFoldersFromServerService: ServiceExecutionProtocol {
   24.34 -    func execute(handler: ServiceFinishedHandler? = nil) {
   24.35 -        let bgID = backgrounder?.beginBackgroundTask(taskName: "SyncFoldersFromServerService")
   24.36 -        let imapLoginOp = LoginImapOperation(parentName: parentName, errorContainer: self,
   24.37 -                                             imapSyncData: imapSyncData)
   24.38 -        let syncFoldersOp = SyncFoldersFromServerOperation(
   24.39 -            parentName: parentName, errorContainer: self, imapSyncData: imapSyncData,
   24.40 -            onlyUpdateIfNecessary: false)
   24.41 -        syncFoldersOp.delegate = self
   24.42 -        syncFoldersOp.addDependency(imapLoginOp)
   24.43 -        syncFoldersOp.completionBlock = { [weak self] in
   24.44 -            syncFoldersOp.completionBlock = nil
   24.45 -            self?.backgrounder?.endBackgroundTask(bgID)
   24.46 -            handler?(self?.error)
   24.47 -        }
   24.48 -        backgroundQueue.addOperations([imapLoginOp, syncFoldersOp], waitUntilFinished: false)
   24.49 -    }
   24.50 -}
    25.1 --- a/pEpForiOS/Network/Service/NetworkServiceWorker.swift	Wed Jul 04 11:24:51 2018 +0200
    25.2 +++ b/pEpForiOS/Network/Service/NetworkServiceWorker.swift	Wed Jul 04 11:27:58 2018 +0200
    25.3 @@ -246,45 +246,54 @@
    25.4          return (sendOp, operations)
    25.5      }
    25.6  
    25.7 -    func buildAppendSendAndDraftOperations(
    25.8 -        imapSyncData: ImapSyncData, errorContainer: ServiceErrorProtocol,
    25.9 -        opImapFinished: Operation, previousOp: Operation) -> (Operation?, [Operation]) {
   25.10 +    func buildAppendOperation(imapSyncData: ImapSyncData,
   25.11 +                              errorContainer: ServiceErrorProtocol) -> Operation {
   25.12 +        let resultOp = BlockOperation {
   25.13 +            let queue = OperationQueue()
   25.14 +            var operations = [Operation]()
   25.15 +            var lastOp = Operation()
   25.16 +            operations.append(lastOp)
   25.17  
   25.18 -        let opAppend = AppendSendMailsOperation(
   25.19 -            parentName: serviceConfig.parentName, imapSyncData: imapSyncData,
   25.20 -            errorContainer: errorContainer)
   25.21 -        opAppend.addDependency(previousOp)
   25.22 -        opImapFinished.addDependency(opAppend)
   25.23 -
   25.24 -        let opDrafts = AppendDraftMailsOperation(
   25.25 -            parentName: serviceConfig.parentName, imapSyncData: imapSyncData,
   25.26 -            errorContainer: errorContainer)
   25.27 -        opDrafts.addDependency(opAppend)
   25.28 -        opImapFinished.addDependency(opDrafts)
   25.29 -
   25.30 -        return (opDrafts, [opAppend, opDrafts])
   25.31 +            MessageModel.performAndWait {
   25.32 +                let folders = AppendMailsOperation.foldersContainingMarkedForAppend(connectInfo:
   25.33 +                    imapSyncData.connectInfo)
   25.34 +                for folder in folders {
   25.35 +                    let op = AppendMailsOperation(folder: folder, imapSyncData: imapSyncData)
   25.36 +                    op.addDependency(lastOp)
   25.37 +                    lastOp = op
   25.38 +                    operations.append(op)
   25.39 +                }
   25.40 +            }
   25.41 +            queue.addOperations(operations, waitUntilFinished: false)
   25.42 +            queue.waitUntilAllOperationsAreFinished()
   25.43 +        }
   25.44 +        return resultOp
   25.45      }
   25.46  
   25.47 -    private func buildUidMoveToFolderOperations(imapSyncData: ImapSyncData,
   25.48 -                                                    errorContainer: ServiceErrorProtocol,
   25.49 -                                                    opImapFinished: Operation,
   25.50 -                                                    previousOp: Operation) -> (Operation?, [Operation]) {
   25.51 -        var lastOp = previousOp
   25.52 -        var createdOps = [MoveToFolderOperation]()
   25.53 -        MessageModel.performAndWait {
   25.54 -            let folders = MoveToFolderOperation.foldersContainingMarkedForMoveToFolder(connectInfo:
   25.55 -                imapSyncData.connectInfo)
   25.56 -            for folder in folders {
   25.57 -                let op = MoveToFolderOperation(imapSyncData: imapSyncData,
   25.58 -                                                      errorContainer: errorContainer,
   25.59 -                                                      folder: folder)
   25.60 -                op.addDependency(lastOp)
   25.61 -                opImapFinished.addDependency(op)
   25.62 -                lastOp = op
   25.63 -                createdOps.append(op)
   25.64 +    func buildUidMoveToFolderOperation(imapSyncData: ImapSyncData,
   25.65 +                                       errorContainer: ServiceErrorProtocol) -> Operation {
   25.66 +        let resultOp = BlockOperation {
   25.67 +            let queue = OperationQueue()
   25.68 +            var operations = [Operation]()
   25.69 +            var lastOp = Operation()
   25.70 +            operations.append(lastOp)
   25.71 +            MessageModel.performAndWait {
   25.72 +                let folders =
   25.73 +                    MoveToFolderOperation.foldersContainingMarkedForMoveToFolder(connectInfo:
   25.74 +                    imapSyncData.connectInfo)
   25.75 +                for folder in folders {
   25.76 +                    let op = MoveToFolderOperation(imapSyncData: imapSyncData,
   25.77 +                                                   errorContainer: errorContainer,
   25.78 +                                                   folder: folder)
   25.79 +                    op.addDependency(lastOp)
   25.80 +                    lastOp = op
   25.81 +                    operations.append(op)
   25.82 +                }
   25.83              }
   25.84 +            queue.addOperations(operations, waitUntilFinished: false)
   25.85 +            queue.waitUntilAllOperationsAreFinished()
   25.86          }
   25.87 -        return (lastOp, createdOps)
   25.88 +        return resultOp
   25.89      }
   25.90  
   25.91      /**
   25.92 @@ -341,38 +350,53 @@
   25.93          return folderInfos
   25.94      }
   25.95  
   25.96 -    func syncExistingMessages(
   25.97 -        folderInfos: [FolderInfo], errorContainer: ServiceErrorProtocol,
   25.98 -        imapSyncData: ImapSyncData,
   25.99 -        onlySyncChangesTriggeredByUser: Bool,
  25.100 -        lastImapOp: Operation, opImapFinished: Operation) -> (lastImapOp: Operation, [Operation]) {
  25.101 -        var theLastImapOp = lastImapOp
  25.102 -        var operations: [Operation] = []
  25.103 -        for fi in folderInfos {
  25.104 -            if let folderID = fi.folderID, let firstUID = fi.firstUID, let lastUID = fi.lastUID,
  25.105 -                firstUID != 0, lastUID != 0, firstUID <= lastUID {
  25.106 -                if !onlySyncChangesTriggeredByUser {
  25.107 -                    let syncMessagesOp = SyncMessagesOperation(
  25.108 -                        parentName: description, errorContainer: errorContainer,
  25.109 -                        imapSyncData: imapSyncData, folderName: fi.name,
  25.110 -                        firstUID: firstUID, lastUID: lastUID)
  25.111 -                    syncMessagesOp.addDependency(theLastImapOp)
  25.112 -                    operations.append(syncMessagesOp)
  25.113 -                    opImapFinished.addDependency(syncMessagesOp)
  25.114 -                    theLastImapOp = syncMessagesOp
  25.115 -                }
  25.116 -                if let syncFlagsOp = SyncFlagsToServerOperation(parentName: description,
  25.117 -                                                                errorContainer: errorContainer,
  25.118 -                                                                imapSyncData: imapSyncData,
  25.119 -                                                                folderID: folderID) {
  25.120 -                    syncFlagsOp.addDependency(theLastImapOp)
  25.121 -                    operations.append(syncFlagsOp)
  25.122 -                    opImapFinished.addDependency(syncFlagsOp)
  25.123 -                    theLastImapOp = syncFlagsOp
  25.124 +    func syncExistingMessagesOperation(folderInfos: [FolderInfo],
  25.125 +                                       errorContainer: ServiceErrorProtocol,
  25.126 +                                       imapSyncData: ImapSyncData,
  25.127 +                                       onlySyncChangesTriggeredByUser: Bool) -> Operation {
  25.128 +        let resultOp = BlockOperation {[weak self] in
  25.129 +            guard let me = self else {
  25.130 +                Log.shared.errorAndCrash(component: #function, errorString: "Lost myself")
  25.131 +                return
  25.132 +            }
  25.133 +            let queue = OperationQueue()
  25.134 +            var operations = [Operation]()
  25.135 +            var lastOp = Operation()
  25.136 +            operations.append(lastOp)
  25.137 +            MessageModel.performAndWait {
  25.138 +                for fi in folderInfos {
  25.139 +                    if let folderID = fi.folderID,
  25.140 +                        let firstUID = fi.firstUID,
  25.141 +                        let lastUID = fi.lastUID,
  25.142 +                        firstUID != 0, lastUID != 0, firstUID <= lastUID {
  25.143 +                        if !onlySyncChangesTriggeredByUser {
  25.144 +                            let syncMessagesOp =
  25.145 +                                SyncMessagesOperation(parentName: me.description,
  25.146 +                                                      errorContainer: errorContainer,
  25.147 +                                                      imapSyncData: imapSyncData,
  25.148 +                                                      folderName: fi.name,
  25.149 +                                                      firstUID: firstUID,
  25.150 +                                                      lastUID: lastUID)
  25.151 +                            syncMessagesOp.addDependency(lastOp)
  25.152 +                            lastOp = syncMessagesOp
  25.153 +                            operations.append(syncMessagesOp)
  25.154 +                        }
  25.155 +                        if let syncFlagsOp =
  25.156 +                            SyncFlagsToServerOperation(parentName: me.description,
  25.157 +                                                       errorContainer: errorContainer,
  25.158 +                                                       imapSyncData: imapSyncData,
  25.159 +                                                       folderID: folderID) {
  25.160 +                            syncFlagsOp.addDependency(lastOp)
  25.161 +                            lastOp = syncFlagsOp
  25.162 +                            operations.append(syncFlagsOp)
  25.163 +                        }
  25.164 +                    }
  25.165                  }
  25.166              }
  25.167 +            queue.addOperations(operations, waitUntilFinished: false)
  25.168 +            queue.waitUntilAllOperationsAreFinished()
  25.169          }
  25.170 -        return (theLastImapOp, operations)
  25.171 +        return resultOp
  25.172      }
  25.173  
  25.174      /// Builds a line of opertations to sync one e-mail account.
  25.175 @@ -411,8 +435,8 @@
  25.176                  #endif
  25.177              }
  25.178          }
  25.179 +        opAllFinished.addDependency(opSmtpFinished)
  25.180          opAllFinished.addDependency(opImapFinished)
  25.181 -        opAllFinished.addDependency(opSmtpFinished)
  25.182  
  25.183          var operations = [Operation]()
  25.184          #if DEBUG
  25.185 @@ -477,20 +501,20 @@
  25.186                  operations.append(opRequiredFolders)
  25.187              }
  25.188              // Client-to-server synchronization (IMAP)
  25.189 -            let (lastAppendSendAndDraftOp, appendSendAndDraftOperations) =
  25.190 -                buildAppendSendAndDraftOperations(
  25.191 -                    imapSyncData: imapSyncData, errorContainer: errorContainer,
  25.192 -                    opImapFinished: opImapFinished, previousOp: lastImapOp)
  25.193 -            lastImapOp = lastAppendSendAndDraftOp ?? lastImapOp
  25.194 -            operations.append(contentsOf: appendSendAndDraftOperations)
  25.195 +            let appendOp = buildAppendOperation(imapSyncData: imapSyncData,
  25.196 +                                                errorContainer: errorContainer)
  25.197 +            appendOp.addDependency(lastImapOp)
  25.198 +            lastImapOp = appendOp
  25.199 +            opImapFinished.addDependency(appendOp)
  25.200 +            operations.append(appendOp)
  25.201  
  25.202 -            let (lastMoveToFolderOp, moveToFolderOperations) =
  25.203 -                buildUidMoveToFolderOperations(imapSyncData: imapSyncData,
  25.204 -                                               errorContainer: errorContainer,
  25.205 -                                               opImapFinished: opImapFinished,
  25.206 -                                               previousOp: lastImapOp)
  25.207 -            lastImapOp = lastMoveToFolderOp ?? lastImapOp
  25.208 -            operations.append(contentsOf: moveToFolderOperations)
  25.209 +
  25.210 +            let moveToFolderOp = buildUidMoveToFolderOperation(imapSyncData: imapSyncData,
  25.211 +                                                               errorContainer: errorContainer)
  25.212 +            moveToFolderOp.addDependency(lastImapOp)
  25.213 +            lastImapOp = moveToFolderOp
  25.214 +            opImapFinished.addDependency(moveToFolderOp)
  25.215 +            operations.append(moveToFolderOp)
  25.216  
  25.217              let folderInfos = determineInterestingFolders(accountInfo: accountInfo)
  25.218  
  25.219 @@ -503,9 +527,6 @@
  25.220                          imapSyncData: imapSyncData, folderName: fi.name) {
  25.221                              [weak self] message in self?.messageFetched(cdMessage: message)
  25.222                      }
  25.223 -                    self.workerQueue.async {
  25.224 -                        Log.info(component: #function, content: "fetchMessagesOp finished")
  25.225 -                    }
  25.226                      fetchMessagesOp.addDependency(lastImapOp)
  25.227                      lastImapOp = fetchMessagesOp
  25.228                      operations.append(fetchMessagesOp)
  25.229 @@ -520,18 +541,69 @@
  25.230                  lastImapOp = opDecrypt
  25.231                  opImapFinished.addDependency(opDecrypt)
  25.232                  operations.append(opDecrypt)
  25.233 +                // In case messages need to be re-uploaded (e.g. for trusted server or extra
  25.234 +                // keys), we want do append, fetch and decrypt them in the same sync cycle.
  25.235 +                let opAppendAndFetchReUploaded = BlockOperation() {[weak self, weak opDecrypt] in
  25.236 +                    guard let me = self, let decryptOp = opDecrypt else {
  25.237 +                        Log.shared.errorAndCrash(component: #function,
  25.238 +                                                 errorString: "Lost...")
  25.239 +                        return
  25.240 +                    }
  25.241 +                    if !decryptOp.didMarkMessagesForReUpload {
  25.242 +                        // Nothing to do.
  25.243 +                        return
  25.244 +                    }
  25.245 +
  25.246 +                    let reUploadQueue = OperationQueue()
  25.247 +                    reUploadQueue.name = "security.pep.networkServiceWorker.ReUploadQueue"
  25.248 +                    var reUploadOperations = [Operation]()
  25.249 +                    var lastOp = Operation()
  25.250 +                    reUploadOperations.append(lastOp)
  25.251 +                    // Append ...
  25.252 +                    let appendOp = me.buildAppendOperation(imapSyncData: imapSyncData,
  25.253 +                                                        errorContainer: errorContainer)
  25.254 +                    appendOp.addDependency(lastOp)
  25.255 +                    lastOp = appendOp
  25.256 +                    reUploadOperations.append(appendOp)
  25.257 +                    // ... fetch ...
  25.258 +                    for fi in folderInfos {
  25.259 +                        let fetchMessagesOp = FetchMessagesOperation(
  25.260 +                            parentName: me.description, errorContainer: errorContainer,
  25.261 +                            imapSyncData: imapSyncData, folderName: fi.name) {
  25.262 +                                [weak self] message in self?.messageFetched(cdMessage: message)
  25.263 +                        }
  25.264 +                        fetchMessagesOp.addDependency(lastOp)
  25.265 +                        lastOp = fetchMessagesOp
  25.266 +                        reUploadOperations.append(fetchMessagesOp)
  25.267 +                    }
  25.268 +
  25.269 +                    // ... and decrypt
  25.270 +                    let opDecrypt = DecryptMessagesOperation(parentName: me.description,
  25.271 +                                                             errorContainer: ErrorContainer())
  25.272 +                    opDecrypt.addDependency(lastOp)
  25.273 +                    lastOp = opDecrypt
  25.274 +                    reUploadOperations.append(opDecrypt)
  25.275 +
  25.276 +                    reUploadQueue.addOperations(reUploadOperations, waitUntilFinished: false)
  25.277 +                    reUploadQueue.waitUntilAllOperationsAreFinished()
  25.278 +                }
  25.279 +                opAppendAndFetchReUploaded.addDependency(lastImapOp)
  25.280 +                lastImapOp = opAppendAndFetchReUploaded
  25.281 +                opImapFinished.addDependency(opAppendAndFetchReUploaded)
  25.282 +                operations.append(opAppendAndFetchReUploaded)
  25.283              }
  25.284  
  25.285              // sync existing messages
  25.286 -            let (lastSyncOp, syncOperations) =
  25.287 -                syncExistingMessages(folderInfos: folderInfos,
  25.288 -                                     errorContainer: errorContainer,
  25.289 -                                     imapSyncData: imapSyncData,
  25.290 -                                     onlySyncChangesTriggeredByUser: onlySyncChangesTriggeredByUser,
  25.291 -                                     lastImapOp: lastImapOp,
  25.292 -                                     opImapFinished: opImapFinished)
  25.293 -            lastImapOp = lastSyncOp
  25.294 -            operations.append(contentsOf: syncOperations)
  25.295 +            let syncExistingMessagesOP =
  25.296 +                syncExistingMessagesOperation(folderInfos: folderInfos,
  25.297 +                                              errorContainer: errorContainer,
  25.298 +                                              imapSyncData: imapSyncData,
  25.299 +                                              onlySyncChangesTriggeredByUser:
  25.300 +                    onlySyncChangesTriggeredByUser)
  25.301 +            syncExistingMessagesOP.addDependency(lastImapOp)
  25.302 +            lastImapOp = syncExistingMessagesOP
  25.303 +            opImapFinished.addDependency(syncExistingMessagesOP)
  25.304 +            operations.append(syncExistingMessagesOP)
  25.305          }
  25.306  
  25.307          operations.append(contentsOf: [opSmtpFinished, opImapFinished, opAllFinished])
    26.1 --- a/pEpForiOS/UI/Background/ReevaluateMessageRatingOperation.swift	Wed Jul 04 11:24:51 2018 +0200
    26.2 +++ b/pEpForiOS/UI/Background/ReevaluateMessageRatingOperation.swift	Wed Jul 04 11:27:58 2018 +0200
    26.3 @@ -47,12 +47,12 @@
    26.4          let theSession = PEPSession()
    26.5          let pepMessage = cdMsg.pEpMessageDict()
    26.6          do {
    26.7 +            let keys = cdMsg.keysFromDecryption?.array as? [String] // Needs to be extented when implementing "Extra Keys" feature to take X-KeyList header into account
    26.8              var newRating = PEP_rating_undefined
    26.9              try theSession.reEvaluateMessageDict(pepMessage,
   26.10 -                                                 xKeyList: nil,
   26.11 +                                                 xKeyList: keys,
   26.12                                                   rating: &newRating,
   26.13                                                   status: nil)
   26.14 -
   26.15              context.updateAndSave(object: cdMsg) {
   26.16                  cdMsg.pEpRating = Int16(newRating.rawValue)
   26.17              }
    27.1 --- a/pEpForiOS/UI/Compose/ComposeTableViewController.swift	Wed Jul 04 11:24:51 2018 +0200
    27.2 +++ b/pEpForiOS/UI/Compose/ComposeTableViewController.swift	Wed Jul 04 11:27:58 2018 +0200
    27.3 @@ -493,6 +493,7 @@
    27.4          }
    27.5  
    27.6          message.pEpProtected = pEpProtection
    27.7 +        message.setOriginalRatingHeader(rating: self.recalculateCurrentRating())
    27.8  
    27.9          return message
   27.10      }
   27.11 @@ -535,7 +536,8 @@
   27.12          return prefixHtml + toWrap + postfixHtml
   27.13      }
   27.14  
   27.15 -    private func recalculateCurrentRating(session: PEPSession) -> PEP_rating {
   27.16 +    private func recalculateCurrentRating() -> PEP_rating {
   27.17 +        let session = PEPSession()
   27.18          if let from = self.origin {
   27.19              currentRating = session.outgoingMessageRating(from: from,
   27.20                                                            to: self.destinyTo,
   27.21 @@ -571,8 +573,7 @@
   27.22      private func calculateComposeColorAndInstallTapGesture() {
   27.23          DispatchQueue.main.async { [weak self] in
   27.24              if let theSelf = self {
   27.25 -                let session = PEPSession()
   27.26 -                let ratingValue = theSelf.recalculateCurrentRating(session: session)
   27.27 +                let ratingValue = theSelf.recalculateCurrentRating()
   27.28                  if let view = theSelf.showPepRating(pEpRating: ratingValue,
   27.29                                                      pEpProtection: theSelf.pEpProtection) {
   27.30                      if theSelf.canHandshake() || theSelf.canToggleProtection() {
    28.1 --- a/pEpForiOS/UI/EmailDisplay/EmailViewController.swift	Wed Jul 04 11:24:51 2018 +0200
    28.2 +++ b/pEpForiOS/UI/EmailDisplay/EmailViewController.swift	Wed Jul 04 11:27:58 2018 +0200
    28.3 @@ -81,9 +81,8 @@
    28.4      }
    28.5  
    28.6      private func showPepRating() {
    28.7 -        let session = PEPSession()
    28.8 -        if let privacyStatusIcon = showPepRating(pEpRating: message?.pEpRating(session: session)) {
    28.9 -            let handshakeCombos = message?.handshakeActionCombinations(session: session) ?? []
   28.10 +        if let privacyStatusIcon = showPepRating(pEpRating: message?.pEpRating()) {
   28.11 +            let handshakeCombos = message?.handshakeActionCombinations() ?? []
   28.12              if !handshakeCombos.isEmpty {
   28.13                  let tapGestureRecognizer = UITapGestureRecognizer(
   28.14                      target: self,
    29.1 --- a/pEpForiOS/UI/MessageThreading/ThreadedFolderWithTop.swift	Wed Jul 04 11:24:51 2018 +0200
    29.2 +++ b/pEpForiOS/UI/MessageThreading/ThreadedFolderWithTop.swift	Wed Jul 04 11:27:58 2018 +0200
    29.3 @@ -25,7 +25,12 @@
    29.4      }
    29.5  
    29.6      func numberOfMessagesInThread(message: Message) -> Int {
    29.7 -        return underlyingThreadedFolder.numberOfMessagesInThread(message: message) + 1
    29.8 +        let baseCount = underlyingThreadedFolder.numberOfMessagesInThread(message: message)
    29.9 +        if baseCount > 0 {
   29.10 +            return baseCount + 1
   29.11 +        } else {
   29.12 +            return baseCount
   29.13 +        }
   29.14      }
   29.15  
   29.16      func messagesInThread(message: Message) -> [Message] {
    30.1 --- a/pEpForiOS/UI/Util/BaseTableViewController.swift	Wed Jul 04 11:24:51 2018 +0200
    30.2 +++ b/pEpForiOS/UI/Util/BaseTableViewController.swift	Wed Jul 04 11:27:58 2018 +0200
    30.3 @@ -19,9 +19,7 @@
    30.4                  // We have no config. Return nonsense.
    30.5                  return AppConfig(
    30.6                      mySelfer: self,
    30.7 -                    messageSyncService: MessageSyncService(sleepTimeInSeconds: 2,
    30.8 -                                                           backgrounder: nil,
    30.9 -                                                           mySelfer: nil),
   30.10 +                    messageSyncService: MessageSyncService(),
   30.11                      errorPropagator: ErrorPropagator(),
   30.12                      oauth2AuthorizationFactory: OAuth2ProviderFactory().oauth2Provider())
   30.13              }
    31.1 --- a/pEpForiOS/Util/Extensions/PEP_rating+Extension.swift	Wed Jul 04 11:24:51 2018 +0200
    31.2 +++ b/pEpForiOS/Util/Extensions/PEP_rating+Extension.swift	Wed Jul 04 11:27:58 2018 +0200
    31.3 @@ -9,6 +9,15 @@
    31.4  import Foundation
    31.5  
    31.6  extension PEP_rating {
    31.7 +
    31.8 +    static func fromString(str: String) -> PEP_rating {
    31.9 +        return PEPSession().rating(from:str)
   31.10 +    }
   31.11 +
   31.12 +    func asString() -> String {
   31.13 +         return PEPSession().string(from: self)
   31.14 +    }
   31.15 +    
   31.16      /**
   31.17       The `PEP_rating`s that should trigger another decryption attempt later on.
   31.18       */
    32.1 --- a/pEpForiOSTests/ContentDispositionTest.swift	Wed Jul 04 11:24:51 2018 +0200
    32.2 +++ b/pEpForiOSTests/ContentDispositionTest.swift	Wed Jul 04 11:27:58 2018 +0200
    32.3 @@ -51,30 +51,24 @@
    32.4          // Create mails from ID1 to ID2 with attachments (inlined or not)
    32.5          let dateBeforeSend = Date().addingTimeInterval(-1.0)
    32.6          let numMailsToSend = 1
    32.7 -        let mailsToSend = try! TestUtil.createOutgoingMails(cdAccount: cdAccount,
    32.8 -                                                            testCase: self,
    32.9 -                                                            numberOfMails: numMailsToSend,
   32.10 -                                                            withAttachments: true,
   32.11 -                                                            attachmentsInlined: attachmentsInlined,
   32.12 -                                                            encrypt: false)
   32.13 +        let mailsToSend = try!
   32.14 +            TestUtil.createOutgoingMails(cdAccount: cdAccount,
   32.15 +                                         fromIdentity: id1,
   32.16 +                                         toIdentity: id2,
   32.17 +                                         setSentTimeOffsetForManualOrdering: false,
   32.18 +                                         testCase: self,
   32.19 +                                         numberOfMails: numMailsToSend,
   32.20 +                                         withAttachments: true,
   32.21 +                                         attachmentsInlined: attachmentsInlined,
   32.22 +                                         encrypt: false,
   32.23 +                                         forceUnencrypted: true)
   32.24          XCTAssertEqual(mailsToSend.count, numMailsToSend)
   32.25 -        for mail in mailsToSend {
   32.26 -            guard let currentReceipinets = mail.to?.array as? [CdIdentity] else {
   32.27 -                XCTFail("Should have receipients")
   32.28 -                return
   32.29 -            }
   32.30 -            mail.from = id1
   32.31 -            mail.removeTos(cdIdentities: currentReceipinets)
   32.32 -            mail.addTo(cdIdentity: id2)
   32.33 -            mail.pEpProtected = false // force unencrypted
   32.34 -            mail.sent = Date()
   32.35 -        }
   32.36 -        Record.saveAndWait()
   32.37  
   32.38          // ... and send them.
   32.39          TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self, skipValidation: true)
   32.40  
   32.41 -        // Sync once again to make sure we mirror the servers state (i.e. receive the sent mails)
   32.42 +        // Sync once again. Just to make sure we mirror the servers state (i.e. receive the
   32.43 +        // sent mails)
   32.44          TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self, skipValidation: true)
   32.45  
   32.46          // Now let's see what we got.
    33.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.2 +++ b/pEpForiOSTests/Features/ReUploadTest.swift	Wed Jul 04 11:27:58 2018 +0200
    33.3 @@ -0,0 +1,474 @@
    33.4 +//
    33.5 +//  TrustedServerTest.swift
    33.6 +//  pEpForiOSTests
    33.7 +//
    33.8 +//  Created by Andreas Buff on 25.06.18.
    33.9 +//  Copyright © 2018 p≡p Security S.A. All rights reserved.
   33.10 +//
   33.11 +
   33.12 +import XCTest
   33.13 +import CoreData
   33.14 +
   33.15 +@testable import MessageModel
   33.16 +@testable import pEpForiOS
   33.17 +
   33.18 +/// Test suite for re-uploading (or not) messages.
   33.19 +/// Find details here: https://dev.pep.security/Common%20App%20Documentation/Trusted_Untrusted_Server_Demystified
   33.20 +class ReUploadTest: CoreDataDrivenTestBase {
   33.21 +    let folderTypesEvaluatedByTests = [FolderType.inbox, .sent]
   33.22 +
   33.23 +    override func setUp() {
   33.24 +        super.setUp()
   33.25 +        // Setup soley mark all mails on server deleted.
   33.26 +        setupSenderAccount()
   33.27 +        markAllMessagesOnServerDeleted()
   33.28 +        // And start from scratch
   33.29 +        tearDownWithoutBotheringXCT()
   33.30 +        setupWithoutBotheringXCT()
   33.31 +        setupSenderAccount()
   33.32 +    }
   33.33 +
   33.34 +    // MARK: - Trusted Server
   33.35 +
   33.36 +    /*
   33.37 +    Send unencrypted
   33.38 +     - Send unencrypted mail
   33.39 +     Check: Is msg in Sent folder unencrypted (*not* encrypted for self)
   33.40 +     */
   33.41 +    func testSendUnencrypted_trustedServer() {
   33.42 +        assert(senderTrusted: true,
   33.43 +               receiverTrusted: false,
   33.44 +               sendEncrypted: false,
   33.45 +               expectedSenderRatingOnServerEncrypted: false,
   33.46 +               expectedSenderRatingToDisplayEncrypted: false,
   33.47 +               expectedReceiverRatingOnServerEncrypted: false,
   33.48 +               expectedReceiverRatingToDisplayEncrypted: false)
   33.49 +    }
   33.50 +
   33.51 +    /*
   33.52 +     Receive unencrypted
   33.53 +     - Receive unencrypted mail
   33.54 +     - Mail is in Inbox unencrypted (*not* encrypted for self)
   33.55 +     - Is msg in Inbox is grey
   33.56 +     */
   33.57 +    func testReceiveUnencrypted_trustedServer() {
   33.58 +        assert(senderTrusted: false,
   33.59 +               receiverTrusted: true,
   33.60 +               sendEncrypted: false,
   33.61 +               expectedSenderRatingOnServerEncrypted: true,
   33.62 +               expectedSenderRatingToDisplayEncrypted: false,
   33.63 +               expectedReceiverRatingOnServerEncrypted: false,
   33.64 +               expectedReceiverRatingToDisplayEncrypted: false)
   33.65 +    }
   33.66 +
   33.67 +    /*
   33.68 +     Send enrcypted
   33.69 +     - Send encrypted mail
   33.70 +     Check:
   33.71 +     - Message is sent encrypted (Inbox receiver)
   33.72 +     - msg in Sent folder is unencrypted (*not* encrypted for self)
   33.73 +     - msg in Sent folder is yelow (*not* grey)
   33.74 +     */
   33.75 +    func testSendEncrypted_trustedServer() {
   33.76 +        assert(senderTrusted: true,
   33.77 +               receiverTrusted: false,
   33.78 +               sendEncrypted: true,
   33.79 +               expectedSenderRatingOnServerEncrypted: false,
   33.80 +               expectedSenderRatingToDisplayEncrypted: true,
   33.81 +               expectedReceiverRatingOnServerEncrypted: true,
   33.82 +               expectedReceiverRatingToDisplayEncrypted: true)
   33.83 +    }
   33.84 +
   33.85 +    /*
   33.86 +     Receive encrypted
   33.87 +     - Receive encrypted mail
   33.88 +     - Mail is in Inbox unencrypted (*not* encrypted for self)
   33.89 +     - msg in Inbox is yellow
   33.90 +     */
   33.91 +    func testReceiveEncrypted_trustedServer() {
   33.92 +        assert(senderTrusted: false,
   33.93 +               receiverTrusted: true,
   33.94 +               sendEncrypted: true,
   33.95 +               expectedSenderRatingOnServerEncrypted: true,
   33.96 +               expectedSenderRatingToDisplayEncrypted: true,
   33.97 +               expectedReceiverRatingOnServerEncrypted: false,
   33.98 +               expectedReceiverRatingToDisplayEncrypted: true)
   33.99 +    }
  33.100 +
  33.101 +    // MARK: - Untrusted Server
  33.102 +
  33.103 +    /*
  33.104 +     Send unencrypted
  33.105 +     - Send unencrypted mail
  33.106 +     Check: Message in Sent folder is encrypted (for self)
  33.107 +     */
  33.108 +    func testSendUnencrypted_untrustedServer() {
  33.109 +        assert(senderTrusted: false,
  33.110 +               receiverTrusted: false,
  33.111 +               sendEncrypted: false,
  33.112 +               expectedSenderRatingOnServerEncrypted: true,
  33.113 +               expectedSenderRatingToDisplayEncrypted: false,
  33.114 +               expectedReceiverRatingOnServerEncrypted: false,
  33.115 +               expectedReceiverRatingToDisplayEncrypted: false)
  33.116 +    }
  33.117 +
  33.118 +    /*
  33.119 +     Receive unencrypted
  33.120 +     - Receive unencrypted mail
  33.121 +     - Mail is in Inbox unencrypted (*not* encrypted for self)
  33.122 +     - Is msg in Inbox is grey
  33.123 +     */
  33.124 +    func testReceiveUnencrypted_untrustedServer() {
  33.125 +        assert(senderTrusted: false,
  33.126 +               receiverTrusted: false,
  33.127 +               sendEncrypted: false,
  33.128 +               expectedSenderRatingOnServerEncrypted: true,
  33.129 +               expectedSenderRatingToDisplayEncrypted: false,
  33.130 +               expectedReceiverRatingOnServerEncrypted: false,
  33.131 +               expectedReceiverRatingToDisplayEncrypted: false)
  33.132 +    }
  33.133 +
  33.134 +    /*
  33.135 +     Send enrcypted
  33.136 +     - Send encrypted mail
  33.137 +     Check:
  33.138 +     - Message is sent encrypted
  33.139 +     - Is msg in Sent folder encrypted (for self)
  33.140 +     - Is msg in Sent folder yellow (not grey)
  33.141 +     */
  33.142 +    func testSendEncrypted_untrustedServer() {
  33.143 +        assert(senderTrusted: false,
  33.144 +               receiverTrusted: false,
  33.145 +               sendEncrypted: true,
  33.146 +               expectedSenderRatingOnServerEncrypted: true,
  33.147 +               expectedSenderRatingToDisplayEncrypted: true,
  33.148 +               expectedReceiverRatingOnServerEncrypted: true,
  33.149 +               expectedReceiverRatingToDisplayEncrypted: true)
  33.150 +    }
  33.151 +
  33.152 +    /*
  33.153 +     Receive encrypted (decryptable)
  33.154 +     - Receive encrypted mail
  33.155 +     - Mail is in Inbox (on server) encrypted (not reuploaded unencrypted)
  33.156 +     - msg color is yellow (not grey)
  33.157 +     */
  33.158 +    func testReceiveEncrypted_untrustedServer() {
  33.159 +        assert(senderTrusted: false,
  33.160 +               receiverTrusted: false,
  33.161 +               sendEncrypted: true,
  33.162 +               expectedSenderRatingOnServerEncrypted: true,
  33.163 +               expectedSenderRatingToDisplayEncrypted: true,
  33.164 +               expectedReceiverRatingOnServerEncrypted: true,
  33.165 +               expectedReceiverRatingToDisplayEncrypted: true)
  33.166 +    }
  33.167 +
  33.168 +    // MARK: - HELPER
  33.169 +
  33.170 +    // Similar to super.setup() but without bothering Xcode Test
  33.171 +    private func setupWithoutBotheringXCT() {
  33.172 +        XCTAssertTrue(PEPUtil.pEpClean())
  33.173 +        persistentSetup = PersistentSetup()
  33.174 +        let cdAccount = SecretTestData().createWorkingCdAccount()
  33.175 +        TestUtil.skipValidation()
  33.176 +        Record.saveAndWait()
  33.177 +        self.cdAccount = cdAccount
  33.178 +    }
  33.179 +
  33.180 +    // Similar to super.tearDown() but without bothering Xcode Test
  33.181 +    private func tearDownWithoutBotheringXCT() {
  33.182 +        imapSyncData?.sync?.close()
  33.183 +        persistentSetup = nil
  33.184 +        PEPSession.cleanup()
  33.185 +    }
  33.186 +
  33.187 +    // MARK: The actual test
  33.188 +
  33.189 +    func assert(senderTrusted: Bool,
  33.190 +                receiverTrusted: Bool,
  33.191 +                sendEncrypted: Bool,
  33.192 +                expectedSenderRatingOnServerEncrypted: Bool,
  33.193 +                expectedSenderRatingToDisplayEncrypted: Bool,
  33.194 +                expectedReceiverRatingOnServerEncrypted: Bool,
  33.195 +                expectedReceiverRatingToDisplayEncrypted: Bool) {
  33.196 +        if senderTrusted {
  33.197 +            TestUtil.setServersTrusted(forCdAccount: cdAccount, testCase: self)
  33.198 +        }
  33.199 +
  33.200 +        guard let sender = cdAccount.identity else {
  33.201 +                XCTFail("No identity")
  33.202 +                return
  33.203 +        }
  33.204 +
  33.205 +        let receiver: CdIdentity
  33.206 +        if sendEncrypted {
  33.207 +            guard let tmpReceiver = createForeignReceiverIdentityKnownKey().cdIdentity() else {
  33.208 +                XCTFail("No identity")
  33.209 +                return
  33.210 +            }
  33.211 +            receiver = tmpReceiver
  33.212 +        } else {
  33.213 +            guard let tmpReceiver = createForeignReceiverIdentityNoKnownKey().cdIdentity() else {
  33.214 +                XCTFail("No identity")
  33.215 +                return
  33.216 +            }
  33.217 +            receiver = tmpReceiver
  33.218 +        }
  33.219 +
  33.220 +        // Send mail and sync
  33.221 +        let sentMails = try! TestUtil.createOutgoingMails(cdAccount: cdAccount,
  33.222 +                                                          fromIdentity: sender,
  33.223 +                                                          toIdentity: receiver,
  33.224 +                                                          setSentTimeOffsetForManualOrdering: false,
  33.225 +                                                          testCase: self,
  33.226 +                                                          numberOfMails: 1,
  33.227 +                                                          withAttachments: false,
  33.228 +                                                          attachmentsInlined: false,
  33.229 +                                                          encrypt: false)
  33.230 +        XCTAssertEqual(sentMails.count, 1)
  33.231 +        guard let sentMail = sentMails.first, let sentUuid = sentMail.uuid else {
  33.232 +            XCTFail("Problem")
  33.233 +            return
  33.234 +        }
  33.235 +        TestUtil.makeFolderInteresting(folderType: .sent, cdAccount: cdAccount)
  33.236 +        TestUtil.syncAndWait(numAccountsToSync: 1, testCase: self, skipValidation: true)
  33.237 +
  33.238 +        let sentFolder = TestUtil.cdFolder(ofType: .sent, in: cdAccount)
  33.239 +        guard let sentFolderName = sentFolder.name else {
  33.240 +            XCTFail("Problem")
  33.241 +            return
  33.242 +        }
  33.243 +        // Everything as expected on sender side?
  33.244 +        guard
  33.245 +            let cdMsg = CdMessage.search(uid: nil,
  33.246 +                                         uuid: sentUuid,
  33.247 +                                         folderName: sentFolderName,
  33.248 +                                         inAccount: cdAccount),
  33.249 +            let msg = cdMsg.message()
  33.250 +            else {
  33.251 +                XCTFail("Message not found")
  33.252 +                return
  33.253 +        }
  33.254 +        XCTAssertTrue(msg.uid > 0, "We fetched the message from server")
  33.255 +
  33.256 +        let senderRatingOnServer = PEPUtil.pEpRatingFromInt(msg.pEpRatingInt)
  33.257 +        if expectedSenderRatingOnServerEncrypted {
  33.258 +            XCTAssertFalse(senderRatingOnServer == PEP_rating_unencrypted,
  33.259 +                           "assumed stored rating on sever")
  33.260 +        } else {
  33.261 +            XCTAssertTrue(senderRatingOnServer == PEP_rating_unencrypted,
  33.262 +                           "assumed stored rating on sever")
  33.263 +        }
  33.264 +
  33.265 +        let senderRatingToDisplay = msg.pEpRating()
  33.266 +        if expectedSenderRatingToDisplayEncrypted {
  33.267 +            XCTAssertFalse(senderRatingToDisplay == PEP_rating_unencrypted,
  33.268 +                           "Color to display to user is correct")
  33.269 +        } else {
  33.270 +            XCTAssertTrue(senderRatingToDisplay == PEP_rating_unencrypted,
  33.271 +                          "Color to display to user is correct")
  33.272 +        }
  33.273 +
  33.274 +        // Fine.
  33.275 +
  33.276 +        // Now lets see on receiver side.
  33.277 +        guard let cdAccountReceiver = createAccountOfReceiver().cdAccount() else {
  33.278 +            XCTFail("No account")
  33.279 +            return
  33.280 +        }
  33.281 +
  33.282 +        if receiverTrusted {
  33.283 +            TestUtil.setServersTrusted(forCdAccount: cdAccountReceiver, testCase: self)
  33.284 +        }
  33.285 +        // Fetch, maybe re-upload and fetch again.
  33.286 +        TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self, skipValidation: true)
  33.287 +
  33.288 +        // Check inbox for the received message
  33.289 +        let inbox = TestUtil.cdFolder(ofType: .inbox, in: cdAccountReceiver)
  33.290 +        guard let inboxFolderName = inbox.name else {
  33.291 +            XCTFail("Problem")
  33.292 +            return
  33.293 +        }
  33.294 +        guard
  33.295 +            let cdReceivedMsg = CdMessage.search(uid: nil,
  33.296 +                                                 uuid: sentUuid,
  33.297 +                                                 folderName: inboxFolderName,
  33.298 +                                                 inAccount: cdAccountReceiver,
  33.299 +                                                 includingDeleted: false),
  33.300 +            let receivedMsg = cdReceivedMsg.message()
  33.301 +            else {
  33.302 +                XCTFail("Message not found")
  33.303 +                return
  33.304 +        }
  33.305 +
  33.306 +        XCTAssertTrue(receivedMsg.uid > 0, "We fetched the message from server")
  33.307 +
  33.308 +        guard let receiverRatingOnServer = PEPUtil.pEpRatingFromInt(receivedMsg.pEpRatingInt) else {
  33.309 +            XCTFail("No rating.")
  33.310 +            return
  33.311 +        }
  33.312 +        if expectedReceiverRatingOnServerEncrypted {
  33.313 +            XCTAssertFalse(receiverRatingOnServer == PEP_rating_unencrypted,
  33.314 +                           "rating on sever")
  33.315 +        } else {
  33.316 +            XCTAssertTrue(receiverRatingOnServer == PEP_rating_unencrypted,
  33.317 +                          "rating on sever")
  33.318 +        }
  33.319 +
  33.320 +        let receiverRatingToDisplay = receivedMsg.pEpRating()
  33.321 +        if expectedReceiverRatingToDisplayEncrypted {
  33.322 +            XCTAssertFalse(receiverRatingToDisplay == PEP_rating_unencrypted,
  33.323 +                           "Color to display to user is correct")
  33.324 +        } else {
  33.325 +            XCTAssertTrue(receiverRatingToDisplay == PEP_rating_unencrypted,
  33.326 +                          "Color to display to user is correct")
  33.327 +        }
  33.328 +    }
  33.329 +
  33.330 +    // MARK: Account / Identity 1 (sender)
  33.331 +
  33.332 +    private func setupSenderAccount() {
  33.333 +        // // Account on trusted server (sender)
  33.334 +        cdAccount.identity?.userName = "unittest.ios.3"
  33.335 +        cdAccount.identity?.userID = "unittest.ios.3_ID"
  33.336 +        cdAccount.identity?.address = "unittest.ios.3@peptest.ch"
  33.337 +        guard
  33.338 +            let cdServerImap = cdAccount.server(type: .imap),
  33.339 +            let imapCredentials = cdServerImap.credentials,
  33.340 +            let cdServerSmtp = cdAccount.server(type: .smtp),
  33.341 +            let smtpCredentials = cdServerSmtp.credentials else {
  33.342 +                XCTFail("Problem in setup")
  33.343 +                return
  33.344 +        }
  33.345 +        imapCredentials.loginName = "unittest.ios.3@peptest.ch"
  33.346 +        smtpCredentials.loginName = "unittest.ios.3@peptest.ch"
  33.347 +        try! TestUtil.importKeyByFileName(session,
  33.348 +                                          fileName:
  33.349 +            "unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_sec.asc")
  33.350 +        try! TestUtil.importKeyByFileName(session,
  33.351 +                                          fileName:
  33.352 +            "unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_pub.asc")
  33.353 +        try! session.setOwnKey(cdAccount.identity!.pEpIdentity(),
  33.354 +                               fingerprint: "550A9E626822040E57CB151A651C4A5DB15B77A3")
  33.355 +        TestUtil.skipValidation()
  33.356 +        Record.saveAndWait()
  33.357 +        cdAccount.createRequiredFoldersAndWait(testCase: self)
  33.358 +    }
  33.359 +
  33.360 +    // MARK: Account / Identity 2 (receiver)
  33.361 +
  33.362 +    private func createForeignReceiverIdentityNoKnownKey() -> Identity {
  33.363 +        let createe = Identity(address: "unittest.ios.4@peptest.ch",
  33.364 +                                 userID: "unittest.ios.4_ID",
  33.365 +                                 addressBookID: nil,
  33.366 +                                 userName: "unittest.ios.4",
  33.367 +                                 isMySelf: false)
  33.368 +        createe.save()
  33.369 +        return createe
  33.370 +    }
  33.371 +
  33.372 +    private func createForeignReceiverIdentityKnownKey() -> Identity {
  33.373 +        let createe = Identity(address: "unittest.ios.4@peptest.ch",
  33.374 +                               userID: "unittest.ios.4_ID",
  33.375 +                               addressBookID: nil,
  33.376 +                               userName: "unittest.ios.4",
  33.377 +                               isMySelf: false)
  33.378 +        try! TestUtil.importKeyByFileName(session,
  33.379 +                                          fileName:
  33.380 +            "unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_pub.asc")
  33.381 +        createe.save()
  33.382 +        return createe
  33.383 +    }
  33.384 +
  33.385 +    private func createOwnIdentityReceiverWithKeys() -> Identity {
  33.386 +        let createe = Identity(address: "unittest.ios.4@peptest.ch",
  33.387 +                               userID: "unittest.ios.4_ID",
  33.388 +                               addressBookID: nil,
  33.389 +                               userName: "unittest.ios.4",
  33.390 +                               isMySelf: true)
  33.391 +        createe.save()
  33.392 +        try! TestUtil.importKeyByFileName(session,
  33.393 +                                          fileName:
  33.394 +            "unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_sec.asc")
  33.395 +        try! TestUtil.importKeyByFileName(session,
  33.396 +                                          fileName:
  33.397 +            "unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_pub.asc")
  33.398 +        try! session.setOwnKey(createe.pEpIdentity(),
  33.399 +                               fingerprint: "66AF 5804 A879 1E01 B407 125A CAF0 D838 1542 49C4")
  33.400 +        return createe
  33.401 +    }
  33.402 +
  33.403 +    private func createAccountOfReceiver(withKeys: Bool = true) -> Account {
  33.404 +        let receiver = createOwnIdentityReceiverWithKeys()
  33.405 +        // Get account from test data. We use it soley to take over server data.
  33.406 +        let tmpCdAccount = SecretTestData().createWorkingCdAccount(number: 1)
  33.407 +        guard
  33.408 +            let cdServerImap = tmpCdAccount.server(type: .imap),
  33.409 +            let imapCredentials = cdServerImap.credentials,
  33.410 +            let cdServerSmtp = tmpCdAccount.server(type: .smtp),
  33.411 +            let smtpCredentials = cdServerSmtp.credentials else {
  33.412 +               fatalError()
  33.413 +        }
  33.414 +        imapCredentials.loginName = receiver.address
  33.415 +        smtpCredentials.loginName = receiver.address
  33.416 +
  33.417 +        let createe = tmpCdAccount.account()
  33.418 +        createe.user = receiver
  33.419 +        // Delete tmp account
  33.420 +        tmpCdAccount.identity?.delete()
  33.421 +        tmpCdAccount.delete()
  33.422 +        Record.saveAndWait()
  33.423 +        // Save new acount
  33.424 +        createe.save()
  33.425 +        TestUtil.skipValidation()
  33.426 +        guard let cdAccount = createe.cdAccount() else {
  33.427 +            XCTFail("No Accoount")
  33.428 +            return createe
  33.429 +        }
  33.430 +        cdAccount.createRequiredFoldersAndWait(testCase: self)
  33.431 +        return createe
  33.432 +    }
  33.433 +
  33.434 +    // MARK: Prepare Messages on Server
  33.435 +
  33.436 +    private func markAllMessagesDeleted(inCdAccount cdAccount: CdAccount) {
  33.437 +        var allMessages = [CdMessage]()
  33.438 +        for type in folderTypesEvaluatedByTests {
  33.439 +            allMessages.append(contentsOf: cdAccount.allMessages(inFolderOfType: type))
  33.440 +        }
  33.441 +        for cdMsg in allMessages {
  33.442 +            let msg = cdMsg.message()
  33.443 +            msg?.imapMarkDeleted()
  33.444 +        }
  33.445 +    }
  33.446 +
  33.447 +    /// As we are using the same servers as trusted and untrusted depending on the test case, we
  33.448 +    /// must not fetch messages that already existed (from previous tests).
  33.449 +    private func markAllMessagesOnServerDeleted() {
  33.450 +        // Create receiver account temporarly to be able to delete all messages.
  33.451 +        // Without keys so the engine does not know it.
  33.452 +        guard let cdAccountReceiver = createAccountOfReceiver(withKeys: false).cdAccount() else {
  33.453 +            XCTFail("No account")
  33.454 +            return
  33.455 +        }
  33.456 +        makeFoldersInteresting(inCdAccount: cdAccountReceiver)
  33.457 +        makeFoldersInteresting(inCdAccount: cdAccount)
  33.458 +        // Fetch all messages.
  33.459 +        TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self, skipValidation: true)
  33.460 +        // Mark all messages deleted ...
  33.461 +        markAllMessagesDeleted(inCdAccount: cdAccountReceiver)
  33.462 +        markAllMessagesDeleted(inCdAccount: cdAccount)
  33.463 +        // ... and propagate the changes to the servers
  33.464 +        TestUtil.syncAndWait(numAccountsToSync: 2, testCase: self, skipValidation: true)
  33.465 +        // Delete receiver account. Has to be freshly crated in tests.
  33.466 +        cdAccountReceiver.delete()
  33.467 +        Record.saveAndWait()
  33.468 +    }
  33.469 +
  33.470 +    // MARK: Other
  33.471 +
  33.472 +    private func makeFoldersInteresting(inCdAccount cdAccount: CdAccount) {
  33.473 +        for type in folderTypesEvaluatedByTests {
  33.474 +            TestUtil.makeFolderInteresting(folderType: type, cdAccount: cdAccount)
  33.475 +        }
  33.476 +    }
  33.477 +}
    34.1 --- a/pEpForiOSTests/FetchFoldersServiceTests.swift	Wed Jul 04 11:24:51 2018 +0200
    34.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.3 @@ -1,92 +0,0 @@
    34.4 -//
    34.5 -//  SyncFoldersFromServerServiceTests.swift
    34.6 -//  pEpForiOS
    34.7 -//
    34.8 -//  Created by Dirk Zimmermann on 03.07.17.
    34.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   34.10 -//
   34.11 -
   34.12 -import XCTest
   34.13 -
   34.14 -@testable import MessageModel
   34.15 -@testable import pEpForiOS
   34.16 -
   34.17 -class SyncFoldersFromServerServiceTests: XCTestCase {
   34.18 -    class SyncFoldersFromServerServiceTestDelegate: SyncFoldersFromServerServiceDelegate {
   34.19 -        var createdFoldersCount = 0
   34.20 -
   34.21 -        func didCreate(folder: Folder) {
   34.22 -            createdFoldersCount += 1
   34.23 -        }
   34.24 -    }
   34.25 -
   34.26 -    var persistentSetup: PersistentSetup!
   34.27 -
   34.28 -    var cdAccount: CdAccount!
   34.29 -    var cdAccountDisfunctional: CdAccount!
   34.30 -
   34.31 -    override func setUp() {
   34.32 -        super.setUp()
   34.33 -        persistentSetup = PersistentSetup()
   34.34 -
   34.35 -        let cdAccount = SecretTestData().createWorkingCdAccount()
   34.36 -        TestUtil.skipValidation()
   34.37 -        Record.saveAndWait()
   34.38 -        self.cdAccount = cdAccount
   34.39 -
   34.40 -        let cdDisfunctionalAccount = SecretTestData().createDisfunctionalCdAccount()
   34.41 -        TestUtil.skipValidation()
   34.42 -        Record.saveAndWait()
   34.43 -        self.cdAccountDisfunctional = cdDisfunctionalAccount
   34.44 -    }
   34.45 -
   34.46 -    func runPrimitiveSyncFolders(shouldSucceed: Bool) {
   34.47 -        let foldersCount1 = (CdFolder.all() ?? []).count
   34.48 -        XCTAssertEqual(foldersCount1, 0)
   34.49 -        let expectationServiceRan = expectation(description: "expectationServiceRan")
   34.50 -        let mbg = MockBackgrounder(expBackgroundTaskFinishedAtLeastOnce: expectationServiceRan)
   34.51 -
   34.52 -        guard let theCdAccount = shouldSucceed ? cdAccount : cdAccountDisfunctional else {
   34.53 -            XCTFail()
   34.54 -            return
   34.55 -        }
   34.56 -        guard let (imapSyncData, _) = TestUtil.syncData(cdAccount: theCdAccount) else {
   34.57 -            XCTFail()
   34.58 -            return
   34.59 -        }
   34.60 -
   34.61 -        let syncService = SyncFoldersFromServerService(
   34.62 -            parentName: #function, backgrounder: mbg, imapSyncData: imapSyncData)
   34.63 -        let syncDelegate = SyncFoldersFromServerServiceTestDelegate()
   34.64 -        syncService.delegate = syncDelegate
   34.65 -
   34.66 -        syncService.execute() { error in
   34.67 -            if shouldSucceed {
   34.68 -                XCTAssertNil(error)
   34.69 -            } else {
   34.70 -                XCTAssertNotNil(error)
   34.71 -            }
   34.72 -        }
   34.73 -
   34.74 -        waitForExpectations(timeout: TestUtil.waitTime) { error in
   34.75 -            XCTAssertNil(error)
   34.76 -        }
   34.77 -
   34.78 -        let foldersCount2 = (CdFolder.all() ?? []).count
   34.79 -        if shouldSucceed {
   34.80 -            XCTAssertGreaterThan(syncDelegate.createdFoldersCount, 0)
   34.81 -            XCTAssertGreaterThan(foldersCount2, foldersCount1)
   34.82 -        } else {
   34.83 -            XCTAssertEqual(syncDelegate.createdFoldersCount, 0)
   34.84 -            XCTAssertEqual(foldersCount1, foldersCount1)
   34.85 -        }
   34.86 -    }
   34.87 -
   34.88 -    func testPrimitiveSyncFoldersOK() {
   34.89 -        runPrimitiveSyncFolders(shouldSucceed: true)
   34.90 -    }
   34.91 -
   34.92 -    func testPrimitiveSyncFoldersError() {
   34.93 -        runPrimitiveSyncFolders(shouldSucceed: false)
   34.94 -    }
   34.95 -}
    35.1 --- a/pEpForiOSTests/FetchMessagesServiceTests.swift	Wed Jul 04 11:24:51 2018 +0200
    35.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.3 @@ -1,66 +0,0 @@
    35.4 -//
    35.5 -//  FetchMessagesServiceTests.swift
    35.6 -//  pEpForiOS
    35.7 -//
    35.8 -//  Created by Dirk Zimmermann on 05.07.17.
    35.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   35.10 -//
   35.11 -
   35.12 -import XCTest
   35.13 -
   35.14 -@testable import MessageModel
   35.15 -@testable import pEpForiOS
   35.16 -
   35.17 -class FetchMessagesServiceTests: XCTestCase {
   35.18 -    var persistentSetup: PersistentSetup!
   35.19 -
   35.20 -    var cdAccount: CdAccount!
   35.21 -
   35.22 -    override func setUp() {
   35.23 -        super.setUp()
   35.24 -        persistentSetup = PersistentSetup()
   35.25 -
   35.26 -        let cdAccount = SecretTestData().createWorkingCdAccount()
   35.27 -        TestUtil.skipValidation()
   35.28 -        Record.saveAndWait()
   35.29 -        self.cdAccount = cdAccount
   35.30 -    }
   35.31 -
   35.32 -    override func tearDown() {
   35.33 -        persistentSetup = nil
   35.34 -    }
   35.35 -
   35.36 -    func testFetchFinishesStrictly() {
   35.37 -        TestUtil.runFetchTest(
   35.38 -            parentName: #function, testCase: self, cdAccount: cdAccount,
   35.39 -            useDisfunctionalAccount: false,
   35.40 -            folderName: "inBOX", expectError: false)
   35.41 -
   35.42 -        persistentSetup = nil
   35.43 -
   35.44 -        // If there are still stray operations for storing messages,
   35.45 -        // the test will fail in the next couple of seconds.
   35.46 -        sleep(UInt32(TestUtil.waitTimeCoupleOfSeconds))
   35.47 -    }
   35.48 -
   35.49 -    func testBasicFetchOK() {
   35.50 -        TestUtil.runFetchTest(
   35.51 -            parentName: #function, testCase: self, cdAccount: cdAccount,
   35.52 -            useDisfunctionalAccount: false,
   35.53 -            folderName: "inBOX", expectError: false)
   35.54 -    }
   35.55 -
   35.56 -    func testBasicFetchAccountError() {
   35.57 -        TestUtil.runFetchTest(
   35.58 -            parentName: #function, testCase: self, cdAccount: cdAccount,
   35.59 -            useDisfunctionalAccount: true,
   35.60 -            folderName: "inBOX", expectError: true)
   35.61 -    }
   35.62 -
   35.63 -    func testBasicFetchError() {
   35.64 -        TestUtil.runFetchTest(
   35.65 -            parentName: #function, testCase: self, cdAccount: cdAccount,
   35.66 -            useDisfunctionalAccount: false,
   35.67 -            folderName: "inBOXeZZZZ", expectError: true)
   35.68 -    }
   35.69 -}
    36.1 --- a/pEpForiOSTests/MessageSyncServiceTests.swift	Wed Jul 04 11:24:51 2018 +0200
    36.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.3 @@ -1,524 +0,0 @@
    36.4 -//
    36.5 -//  MessageSyncServiceTests.swift
    36.6 -//  pEpForiOS
    36.7 -//
    36.8 -//  Created by Dirk Zimmermann on 29.06.17.
    36.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   36.10 -//
   36.11 -
   36.12 -import XCTest
   36.13 -import CoreData
   36.14 -
   36.15 -@testable import MessageModel
   36.16 -@testable import pEpForiOS
   36.17 -
   36.18 -class MessageSyncServiceTests: CoreDataDrivenTestBase {
   36.19 -    var cdAccountDisfunctional: CdAccount!
   36.20 -    var messageSyncService: MessageSyncService?
   36.21 -
   36.22 -    override func setUp() {
   36.23 -        super.setUp()
   36.24 -        continueAfterFailure = false
   36.25 -    }
   36.26 -
   36.27 -    override func tearDown() {
   36.28 -        messageSyncService?.cancel()
   36.29 -        messageSyncService = nil
   36.30 -        super.tearDown()
   36.31 -    }
   36.32 -
   36.33 -    //MARK: - Test Delegates
   36.34 -
   36.35 -    class TestErrorDelegate: MessageSyncServiceErrorDelegate {
   36.36 -        var expErrorOccurred: XCTestExpectation?
   36.37 -        var error: Error?
   36.38 -
   36.39 -        init(expErrorOccurred: XCTestExpectation? = nil) {
   36.40 -            self.expErrorOccurred = expErrorOccurred
   36.41 -        }
   36.42 -
   36.43 -        func show(error: Error) {
   36.44 -            self.error = error
   36.45 -            if let expError = expErrorOccurred {
   36.46 -                expError.fulfill()
   36.47 -            } else {
   36.48 -                XCTFail("Send error: \(error)")
   36.49 -            }
   36.50 -        }
   36.51 -    }
   36.52 -
   36.53 -    class TestSentDelegate: MessageSyncServiceSentDelegate {
   36.54 -        let expMessagesSent: XCTestExpectation?
   36.55 -
   36.56 -        var requestedMessagesSent = Set<Message>()
   36.57 -        var messageIDsSent = Set<MessageID>()
   36.58 -
   36.59 -        init(expMessagesSent: XCTestExpectation?) {
   36.60 -            self.expMessagesSent = expMessagesSent
   36.61 -        }
   36.62 -
   36.63 -        func didSend(message: Message) {
   36.64 -            if requestedMessagesSent.contains(message) {
   36.65 -                XCTFail("message sent double")
   36.66 -            } else {
   36.67 -                requestedMessagesSent.insert(message)
   36.68 -            }
   36.69 -        }
   36.70 -
   36.71 -        func didSend(messageIDs: [MessageID]) {
   36.72 -            messageIDsSent = messageIDsSent.union(messageIDs)
   36.73 -            expMessagesSent?.fulfill()
   36.74 -        }
   36.75 -    }
   36.76 -
   36.77 -    class TestSyncDelegate: MessageSyncServiceSyncDelegate {
   36.78 -        let expMessagesSynced: XCTestExpectation
   36.79 -
   36.80 -        init(expMessagesSynced: XCTestExpectation) {
   36.81 -            self.expMessagesSynced = expMessagesSynced
   36.82 -        }
   36.83 -
   36.84 -        func didSync(account: Account) {
   36.85 -            expMessagesSynced.fulfill()
   36.86 -        }
   36.87 -    }
   36.88 -
   36.89 -    class TestAccountVerificationDelegate: AccountVerificationServiceDelegate {
   36.90 -        let expAccountVerified: XCTestExpectation
   36.91 -        var verificationResult: AccountVerificationResult?
   36.92 -
   36.93 -        init(expAccountVerified: XCTestExpectation) {
   36.94 -            self.expAccountVerified = expAccountVerified
   36.95 -        }
   36.96 -
   36.97 -        func verified(account: Account, service: AccountVerificationServiceProtocol,
   36.98 -                      result: AccountVerificationResult) {
   36.99 -            self.verificationResult = result
  36.100 -            expAccountVerified.fulfill()
  36.101 -        }
  36.102 -    }
  36.103 -
  36.104 -    class TestStateDelegate: MessageSyncServiceStateDelegate {
  36.105 -        let expReachedIdling: XCTestExpectation?
  36.106 -
  36.107 -        init(expReachedIdling: XCTestExpectation?) {
  36.108 -            self.expReachedIdling = expReachedIdling
  36.109 -        }
  36.110 -
  36.111 -        func startIdling(account: Account) {
  36.112 -            expReachedIdling?.fulfill()
  36.113 -        }
  36.114 -    }
  36.115 -
  36.116 -    class TestFlagsDelegate: MessageSyncFlagsUploadDelegate {
  36.117 -        var messagesChanged = Set<Message>()
  36.118 -
  36.119 -        func flagsUploaded(message: Message) {
  36.120 -            messagesChanged.insert(message)
  36.121 -        }
  36.122 -    }
  36.123 -
  36.124 -    class MessageFolderTestDelegate: MessageFolderDelegate {
  36.125 -        let expMessagesDeleted: XCTestExpectation?
  36.126 -        var deletedMessages = Set<MessageFolder>()
  36.127 -
  36.128 -        init(expMessagesDeleted: XCTestExpectation?) {
  36.129 -            self.expMessagesDeleted = expMessagesDeleted
  36.130 -        }
  36.131 -
  36.132 -        func didUpdate(messageFolder: MessageFolder) {
  36.133 -            //do nothing
  36.134 -        }
  36.135 -        func didCreate(messageFolder: MessageFolder) {
  36.136 -            //do nothing
  36.137 -        }
  36.138 -        func didDelete(messageFolder: MessageFolder) {
  36.139 -            deletedMessages.insert(messageFolder)
  36.140 -        }
  36.141 -    }
  36.142 -
  36.143 -    //MARK: - Tests
  36.144 -
  36.145 -    func testBasicPassiveSend() {
  36.146 -        runMessageSyncServiceSend(
  36.147 -            parentName: #function,
  36.148 -            cdAccount: cdAccount,
  36.149 -            numberOfOutgoingMessagesToCreate: 3,
  36.150 -            numberOfOutgoingMessagesToSendImmediately: 0,
  36.151 -            numberOfOutgoingMessagesToSendLater: 0,
  36.152 -            expectedNumberOfExpectedBackgroundTasks: 5,
  36.153 -            expectedNumberOfSyncs: 1)
  36.154 -    }
  36.155 -
  36.156 -    func testSendSeveral() {
  36.157 -        runMessageSyncServiceSend(
  36.158 -            parentName: #function,
  36.159 -            cdAccount: cdAccount,
  36.160 -            numberOfOutgoingMessagesToCreate: 3,
  36.161 -            numberOfOutgoingMessagesToSendImmediately: 2,
  36.162 -            numberOfOutgoingMessagesToSendLater: 0,
  36.163 -            expectedNumberOfExpectedBackgroundTasks: 6,
  36.164 -            expectedNumberOfSyncs: 1)
  36.165 -    }
  36.166 -
  36.167 -    func testSyncWithUnverifiedAccount() {
  36.168 -        let expMessagesSynced = expectation(description: "expMessagesSynced")
  36.169 -        let syncDelegate = TestSyncDelegate(expMessagesSynced: expMessagesSynced)
  36.170 -
  36.171 -        let ms = MessageSyncService(
  36.172 -            sleepTimeInSeconds: 2, parentName: #function, backgrounder: nil, mySelfer: nil)
  36.173 -        ms.errorDelegate = TestErrorDelegate()
  36.174 -        messageSyncService = ms
  36.175 -        ms.syncDelegate = syncDelegate
  36.176 -        ms.start(account: cdAccount.account())
  36.177 -
  36.178 -        waitForExpectations(timeout: TestUtil.waitTime) { error in
  36.179 -            XCTAssertNil(error)
  36.180 -        }
  36.181 -
  36.182 -        ms.cancel(account: cdAccount.account())
  36.183 -    }
  36.184 -
  36.185 -    func testSyncWithErroneousAccount() {
  36.186 -        let expErrorOccurred = expectation(description: "expErrorOccurred")
  36.187 -        let errorDelegate = TestErrorDelegate(expErrorOccurred: expErrorOccurred)
  36.188 -
  36.189 -        TestUtil.makeServersUnreachable(cdAccount: cdAccount)
  36.190 -
  36.191 -        let ms = MessageSyncService(
  36.192 -            sleepTimeInSeconds: 2, parentName: #function, backgrounder: nil, mySelfer: nil)
  36.193 -        ms.errorDelegate = TestErrorDelegate()
  36.194 -        messageSyncService = ms
  36.195 -        ms.errorDelegate = errorDelegate
  36.196 -        ms.start(account: cdAccount.account())
  36.197 -
  36.198 -        waitForExpectations(timeout: TestUtil.waitTime) { error in
  36.199 -            XCTAssertNil(error)
  36.200 -        }
  36.201 -
  36.202 -        XCTAssertNotNil(errorDelegate.error)
  36.203 -
  36.204 -        ms.cancel(account: cdAccount.account())
  36.205 -    }
  36.206 -
  36.207 -    func testTypicalNewAccountSetup() {
  36.208 -        let ms = MessageSyncService(
  36.209 -            sleepTimeInSeconds: 2, parentName: #function, backgrounder: nil, mySelfer: nil)
  36.210 -        messageSyncService = ms
  36.211 -        ms.errorDelegate = TestErrorDelegate()
  36.212 -
  36.213 -        // Verification
  36.214 -        let expVerified = expectation(description: "expVerified")
  36.215 -        let verificationDelegate = TestAccountVerificationDelegate(expAccountVerified: expVerified)
  36.216 -        ms.requestVerification(account: cdAccount.account(), delegate: verificationDelegate)
  36.217 -        waitForExpectations(timeout: TestUtil.waitTime) { error in
  36.218 -            XCTAssertNil(error)
  36.219 -        }
  36.220 -        guard let result = verificationDelegate.verificationResult else {
  36.221 -            XCTFail()
  36.222 -            return
  36.223 -        }
  36.224 -        switch result {
  36.225 -        case .ok:
  36.226 -            break
  36.227 -        default:
  36.228 -            XCTFail("Unexpected verification result: \(result)")
  36.229 -        }
  36.230 -
  36.231 -        runMessageSyncWithSend(
  36.232 -            ms: ms, cdAccount: cdAccount, numberOfOutgoingMessagesToCreate: 3,
  36.233 -            numberOfOutgoingMessagesToSendImmediately: 3,
  36.234 -            numberOfOutgoingMessagesToSendLater: 0,
  36.235 -            expectedNumberOfSyncs: 1)
  36.236 -
  36.237 -        ms.cancel(account: cdAccount.account())
  36.238 -    }
  36.239 -
  36.240 -    func testSendOnIdle() {
  36.241 -        let ms = runOrContinueUntilIdle(parentName: #function)
  36.242 -        sendMessages(ms: ms)
  36.243 -        let _ = runOrContinueUntilIdle(parentName: #function, messageSyncService: ms)
  36.244 -        ReferenceCounter.logOutstanding()
  36.245 -    }
  36.246 -
  36.247 -    func testUploadFlagsOnIdle() {
  36.248 -        let context = Record.Context.default
  36.249 -        let ms = runOrContinueUntilIdle(parentName: #function)
  36.250 -
  36.251 -        guard let cdFolder = CdFolder.by(
  36.252 -            folderType: .inbox, account: cdAccount, context: context) else {
  36.253 -                XCTFail()
  36.254 -                return
  36.255 -        }
  36.256 -
  36.257 -        uploadFlags(context: context, ms: ms, cdFolder: cdFolder, maxCount: 3)
  36.258 -    }
  36.259 -
  36.260 -    func notestUploadFlagsBeforeIdle() {
  36.261 -        let ms = runOrContinueUntilIdle(parentName: #function)
  36.262 -
  36.263 -        let context = Record.Context.default
  36.264 -
  36.265 -        guard let cdFolder = CdFolder.by(
  36.266 -            folderType: .inbox, account: cdAccount, context: context) else {
  36.267 -                XCTFail()
  36.268 -                return
  36.269 -        }
  36.270 -
  36.271 -        sendMessages(ms: ms)
  36.272 -        let expectedNumberOfFlagUploads = 3
  36.273 -        let flagsDelegate = TestFlagsDelegate()
  36.274 -        uploadFlags(
  36.275 -            context: context, ms: ms, cdFolder: cdFolder, maxCount: expectedNumberOfFlagUploads,
  36.276 -            flagsDelegate: flagsDelegate, waitForAnswer: false)
  36.277 -        let _ = runOrContinueUntilIdle(parentName: #function, messageSyncService: ms)
  36.278 -        XCTAssertEqual(flagsDelegate.messagesChanged.count, expectedNumberOfFlagUploads)
  36.279 -        ms.stateDelegate = nil
  36.280 -    }
  36.281 -
  36.282 -    func notestIdle() {
  36.283 -        let messageFolderDelegate = MessageFolderTestDelegate(expMessagesDeleted: nil)
  36.284 -        MessageModelConfig.messageFolderDelegate = messageFolderDelegate
  36.285 -
  36.286 -        runMessageSyncServiceSend(
  36.287 -            parentName: #function,
  36.288 -            cdAccount: cdAccount,
  36.289 -            numberOfOutgoingMessagesToCreate: 0,
  36.290 -            numberOfOutgoingMessagesToSendImmediately: 0,
  36.291 -            numberOfOutgoingMessagesToSendLater: 0,
  36.292 -            expectedNumberOfExpectedBackgroundTasks: -1,
  36.293 -            expectedNumberOfSyncs: 2)
  36.294 -
  36.295 -        print("Deleted messages: \(messageFolderDelegate.deletedMessages.count)")
  36.296 -    }
  36.297 -
  36.298 -    // MARK: - Helpers
  36.299 -
  36.300 -    func sendMessages(ms: MessageSyncService) {
  36.301 -        let numberOfOutgoingMessagesToCreate = 3
  36.302 -        let numberOfOutgoingMessagesToSendImmediately = numberOfOutgoingMessagesToCreate
  36.303 -        let outgoingCdMsgs = try! TestUtil.createOutgoingMails(
  36.304 -            cdAccount: cdAccount, testCase: self,
  36.305 -            numberOfMails: numberOfOutgoingMessagesToCreate)
  36.306 -
  36.307 -        if outgoingCdMsgs.count < numberOfOutgoingMessagesToCreate {
  36.308 -            XCTFail()
  36.309 -            return
  36.310 -        }
  36.311 -
  36.312 -        let cdMsgsToSend = outgoingCdMsgs.prefix(numberOfOutgoingMessagesToSendImmediately)
  36.313 -        XCTAssertEqual(cdMsgsToSend.count, numberOfOutgoingMessagesToSendImmediately)
  36.314 -        var msgsToSend = Set<Message>()
  36.315 -        for cdMsg in cdMsgsToSend {
  36.316 -            guard let msg = cdMsg.message() else {
  36.317 -                XCTFail()
  36.318 -                return
  36.319 -            }
  36.320 -            msgsToSend.insert(msg)
  36.321 -            ms.requestSend(message: msg)
  36.322 -        }
  36.323 -    }
  36.324 -
  36.325 -    func uploadFlags(
  36.326 -        context: NSManagedObjectContext, ms: MessageSyncService, cdMessage: CdMessage,
  36.327 -        flagsDelegate: TestFlagsDelegate = TestFlagsDelegate(), waitForAnswer: Bool) {
  36.328 -        guard
  36.329 -            let cdLocalFlags1 = cdMessage.imapFields().localFlags,
  36.330 -            let cdServerFlags1 = cdMessage.imapFields().serverFlags,
  36.331 -            let cdFolder = cdMessage.parent else {
  36.332 -                XCTFail()
  36.333 -                return
  36.334 -        }
  36.335 -        cdLocalFlags1.flagFlagged = !cdLocalFlags1.flagFlagged
  36.336 -        let expectedFlagged = cdLocalFlags1.flagFlagged
  36.337 -        XCTAssertNotEqual(cdLocalFlags1.flagFlagged, cdServerFlags1.flagFlagged)
  36.338 -        context.saveAndLogErrors()
  36.339 -
  36.340 -        let cdSyncMsgs1 = SyncFlagsToServerOperation.messagesToBeSynced(
  36.341 -            folder: cdFolder, context: Record.Context.background)
  36.342 -        XCTAssertGreaterThan(cdSyncMsgs1.count, 0)
  36.343 -
  36.344 -        guard let msg = cdMessage.message() else {
  36.345 -            XCTFail()
  36.346 -            return
  36.347 -        }
  36.348 -        ms.flagsUploadDelegate = flagsDelegate
  36.349 -        ms.requestFlagChange(message: msg)
  36.350 -
  36.351 -        if waitForAnswer {
  36.352 -            let _ = runOrContinueUntilIdle(parentName: #function, messageSyncService: ms)
  36.353 -            XCTAssertTrue(flagsDelegate.messagesChanged.contains(msg))
  36.354 -            context.refresh(cdMessage, mergeChanges: true)
  36.355 -            guard
  36.356 -                let cdLocalFlags2 = cdMessage.imapFields().localFlags,
  36.357 -                let cdServerFlags2 = cdMessage.imapFields().serverFlags else {
  36.358 -                    XCTFail()
  36.359 -                    return
  36.360 -            }
  36.361 -            XCTAssertEqual(cdLocalFlags2.flagFlagged, cdServerFlags2.flagFlagged)
  36.362 -            XCTAssertEqual(cdLocalFlags2.flagFlagged, expectedFlagged)
  36.363 -        }
  36.364 -    }
  36.365 -
  36.366 -    func uploadFlags(context: NSManagedObjectContext,
  36.367 -                     ms: MessageSyncService,
  36.368 -                     cdFolder: CdFolder, maxCount: Int,
  36.369 -                     flagsDelegate: TestFlagsDelegate = TestFlagsDelegate(),
  36.370 -                     waitForAnswer: Bool = true) {
  36.371 -        let cdMessages = cdFolder.messages?.sortedArray(
  36.372 -            using: [NSSortDescriptor(key: "uid", ascending: true)]) as? [CdMessage] ?? []
  36.373 -        XCTAssertGreaterThan(cdMessages.count, 0)
  36.374 -
  36.375 -        for cdM in cdMessages.prefix(maxCount) {
  36.376 -            uploadFlags(context: context, ms: ms, cdMessage: cdM,
  36.377 -                        flagsDelegate: flagsDelegate, waitForAnswer: waitForAnswer)
  36.378 -        }
  36.379 -    }
  36.380 -
  36.381 -    func send(messageSyncService ms: MessageSyncService, messages: [Message],
  36.382 -              numberOfTotalOutgoingMessages: Int, expectedNumberOfSyncs: Int) {
  36.383 -        let errorDelegate = TestErrorDelegate()
  36.384 -        ms.errorDelegate = errorDelegate
  36.385 -
  36.386 -        var sentDelegate: TestSentDelegate?
  36.387 -        if numberOfTotalOutgoingMessages > 0 {
  36.388 -            let expMessagesSent = expectation(description: "expMessagesSent")
  36.389 -            expMessagesSent.expectedFulfillmentCount = expectedNumberOfSyncs
  36.390 -            sentDelegate = TestSentDelegate(expMessagesSent: expMessagesSent)
  36.391 -            ms.sentDelegate = sentDelegate
  36.392 -        }
  36.393 -
  36.394 -        let expMessagesSynced = expectation(description: "expMessagesSynced")
  36.395 -        expMessagesSynced.expectedFulfillmentCount = expectedNumberOfSyncs
  36.396 -        let syncDelegate = TestSyncDelegate(expMessagesSynced: expMessagesSynced)
  36.397 -        ms.syncDelegate = syncDelegate
  36.398 -
  36.399 -        let expReachedIdle = expectation(description: "expReachedIdle")
  36.400 -        expReachedIdle.expectedFulfillmentCount = expectedNumberOfSyncs
  36.401 -        let stateDelegate = TestStateDelegate(expReachedIdling: expReachedIdle)
  36.402 -        ms.stateDelegate = stateDelegate
  36.403 -
  36.404 -        ms.start(account: cdAccount.account())
  36.405 -
  36.406 -        for msg in messages {
  36.407 -            ms.requestSend(message: msg)
  36.408 -        }
  36.409 -
  36.410 -        waitForExpectations(timeout: TestUtil.waitTime) { error in
  36.411 -            XCTAssertNil(error)
  36.412 -        }
  36.413 -
  36.414 -        XCTAssertNil(errorDelegate.error)
  36.415 -        if let sd = sentDelegate {
  36.416 -            XCTAssertEqual(sd.requestedMessagesSent.count, messages.count)
  36.417 -            XCTAssertEqual(sd.messageIDsSent.count, numberOfTotalOutgoingMessages)
  36.418 -            for msg in messages {
  36.419 -                XCTAssertTrue(sd.messageIDsSent.contains(msg.messageID))
  36.420 -            }
  36.421 -        }
  36.422 -    }
  36.423 -
  36.424 -    func runMessageSyncWithSend(ms: MessageSyncService,
  36.425 -                                cdAccount theCdAccount: CdAccount,
  36.426 -                                numberOfOutgoingMessagesToCreate: Int,
  36.427 -                                numberOfOutgoingMessagesToSendImmediately: Int,
  36.428 -                                numberOfOutgoingMessagesToSendLater: Int,
  36.429 -                                expectedNumberOfSyncs: Int) {
  36.430 -        let outgoingCdMsgs = try! TestUtil.createOutgoingMails(
  36.431 -            cdAccount: theCdAccount, testCase: self,
  36.432 -            numberOfMails: numberOfOutgoingMessagesToCreate)
  36.433 -
  36.434 -        if outgoingCdMsgs.count < numberOfOutgoingMessagesToCreate {
  36.435 -            XCTFail()
  36.436 -            return
  36.437 -        }
  36.438 -
  36.439 -        let cdMsgsToSend = outgoingCdMsgs.prefix(numberOfOutgoingMessagesToSendImmediately)
  36.440 -        XCTAssertEqual(cdMsgsToSend.count, numberOfOutgoingMessagesToSendImmediately)
  36.441 -        var msgsToSend = [Message]()
  36.442 -        for cdMsg in cdMsgsToSend {
  36.443 -            guard let msg = cdMsg.message() else {
  36.444 -                XCTFail()
  36.445 -                return
  36.446 -            }
  36.447 -            msgsToSend.append(msg)
  36.448 -        }
  36.449 -
  36.450 -        send(messageSyncService: ms, messages: msgsToSend,
  36.451 -             numberOfTotalOutgoingMessages: outgoingCdMsgs.count,
  36.452 -             expectedNumberOfSyncs: expectedNumberOfSyncs)
  36.453 -
  36.454 -        if numberOfOutgoingMessagesToSendLater <= 0 {
  36.455 -            return
  36.456 -        }
  36.457 -
  36.458 -        let outgoingCdMsgs2 = try! TestUtil.createOutgoingMails(
  36.459 -            cdAccount: theCdAccount, testCase: self,
  36.460 -            numberOfMails: numberOfOutgoingMessagesToSendLater)
  36.461 -        if outgoingCdMsgs2.count < numberOfOutgoingMessagesToSendLater {
  36.462 -            XCTFail()
  36.463 -            return
  36.464 -        }
  36.465 -        let outgoingMessages = outgoingCdMsgs2.flatMap() { return $0.message() }
  36.466 -        send(messageSyncService: ms, messages: outgoingMessages,
  36.467 -             numberOfTotalOutgoingMessages: outgoingMessages.count, expectedNumberOfSyncs: 1)
  36.468 -    }
  36.469 -
  36.470 -    func runMessageSyncServiceSend(parentName: String,
  36.471 -                                   cdAccount theCdAccount: CdAccount,
  36.472 -                                   numberOfOutgoingMessagesToCreate: Int,
  36.473 -                                   numberOfOutgoingMessagesToSendImmediately: Int,
  36.474 -                                   numberOfOutgoingMessagesToSendLater: Int,
  36.475 -                                   expectedNumberOfExpectedBackgroundTasks: Int,
  36.476 -                                   expectedNumberOfSyncs: Int) {
  36.477 -        var mbg: MockBackgrounder?
  36.478 -        if expectedNumberOfExpectedBackgroundTasks > 0 {
  36.479 -            let expBackgroundTaskFinished = expectation(description: "expBackgrounded")
  36.480 -            expBackgroundTaskFinished.expectedFulfillmentCount =
  36.481 -                expectedNumberOfExpectedBackgroundTasks
  36.482 -            mbg = MockBackgrounder(expBackgroundTaskFinishedAtLeastOnce: expBackgroundTaskFinished)
  36.483 -        }
  36.484 -        let ms = MessageSyncService(
  36.485 -            sleepTimeInSeconds: 2, parentName: parentName, backgrounder: mbg, mySelfer: nil)
  36.486 -        ms.errorDelegate = TestErrorDelegate()
  36.487 -        messageSyncService = ms
  36.488 -
  36.489 -        runMessageSyncWithSend(
  36.490 -            ms: ms, cdAccount: cdAccount,
  36.491 -            numberOfOutgoingMessagesToCreate: numberOfOutgoingMessagesToCreate,
  36.492 -            numberOfOutgoingMessagesToSendImmediately: numberOfOutgoingMessagesToSendImmediately,
  36.493 -            numberOfOutgoingMessagesToSendLater: numberOfOutgoingMessagesToSendLater,
  36.494 -            expectedNumberOfSyncs: expectedNumberOfSyncs)
  36.495 -
  36.496 -        if let backgrounder = mbg {
  36.497 -            XCTAssertEqual(backgrounder.numberOfBackgroundTasksOutstanding, 0)
  36.498 -            XCTAssertEqual(backgrounder.totalNumberOfBackgroundTasksFinished,
  36.499 -                           expectedNumberOfExpectedBackgroundTasks)
  36.500 -            XCTAssertEqual(backgrounder.totalNumberOfBackgroundTasksStarted,
  36.501 -                           backgrounder.totalNumberOfBackgroundTasksFinished)
  36.502 -        }
  36.503 -
  36.504 -        ms.cancel(account: cdAccount.account())
  36.505 -    }
  36.506 -
  36.507 -    func runOrContinueUntilIdle(parentName: String,
  36.508 -                                messageSyncService: MessageSyncService? = nil) -> MessageSyncService {
  36.509 -        let ms = messageSyncService ?? MessageSyncService(
  36.510 -            sleepTimeInSeconds: 2, parentName: parentName, backgrounder: nil, mySelfer: nil)
  36.511 -        self.messageSyncService = ms
  36.512 -        let expReachedIdle = expectation(description: "expReachedIdle")
  36.513 -        expReachedIdle.expectedFulfillmentCount = 1
  36.514 -        let stateDelegate = TestStateDelegate(expReachedIdling: expReachedIdle)
  36.515 -        ms.stateDelegate = stateDelegate
  36.516 -        
  36.517 -        if messageSyncService == nil {
  36.518 -            ms.errorDelegate = TestErrorDelegate()
  36.519 -            ms.start(account: cdAccount.account())
  36.520 -        }
  36.521 -        waitForExpectations(timeout: TestUtil.waitTime) { error in
  36.522 -            XCTAssertNil(error)
  36.523 -        }
  36.524 -        ms.stateDelegate = nil
  36.525 -        return ms
  36.526 -    }
  36.527 -}
    37.1 --- a/pEpForiOSTests/Models/EmailListViewModel+ThreadingTests.swift	Wed Jul 04 11:24:51 2018 +0200
    37.2 +++ b/pEpForiOSTests/Models/EmailListViewModel+ThreadingTests.swift	Wed Jul 04 11:27:58 2018 +0200
    37.3 @@ -248,6 +248,41 @@
    37.4          }
    37.5      }
    37.6  
    37.7 +    func testThreadedReferencesSentMessage() {
    37.8 +        FolderThreading.override(factory: ThreadAwareFolderFactory())
    37.9 +        setUpTopMessages()
   37.10 +
   37.11 +        let sentFolder = Folder.init(name: "Sent",
   37.12 +                                     parent: nil,
   37.13 +                                     account: account,
   37.14 +                                     folderType: .sent)
   37.15 +        sentFolder.save()
   37.16 +
   37.17 +        let sentMessage = createMessage(number: nextUid(), inFolder: sentFolder)
   37.18 +        sentMessage.save()
   37.19 +
   37.20 +        let topMessageReferencingSentMessage = topMessages[0]
   37.21 +        topMessageReferencingSentMessage.references.append(sentMessage.messageID)
   37.22 +        topMessageReferencingSentMessage.save()
   37.23 +
   37.24 +        guard let referencedSentMessageOrig =
   37.25 +            topMessageReferencingSentMessage.referencedMessages().first else {
   37.26 +                XCTFail()
   37.27 +                return
   37.28 +        }
   37.29 +        XCTAssertEqual(referencedSentMessageOrig.messageID, sentMessage.messageID)
   37.30 +
   37.31 +        let incomingMessage = testIncomingMessage(references: [sentMessage],
   37.32 +                                                  indexPathUpdated: indexOfTopMessage0)
   37.33 +
   37.34 +        let incomingMessageReferencedMessages = incomingMessage.referencedMessages()
   37.35 +        guard let referencedSentMessageIncoming = incomingMessageReferencedMessages.first else {
   37.36 +                XCTFail()
   37.37 +                return
   37.38 +        }
   37.39 +        XCTAssertEqual(referencedSentMessageIncoming.messageID, sentMessage.messageID)
   37.40 +    }
   37.41 +
   37.42      // MARK: - Internal - Helpers
   37.43  
   37.44      func setUpTopMessages() {
   37.45 @@ -285,12 +320,28 @@
   37.46          XCTAssertNil(emailListViewModel.currentDisplayedMessage?.messageModel)
   37.47      }
   37.48  
   37.49 +    func latestUid() -> Int {
   37.50 +        var highestUid: Int32 = 0
   37.51 +        if let allCdMessages = CdMessage.all() as? [CdMessage] {
   37.52 +            for cdMsg in allCdMessages {
   37.53 +                if cdMsg.uid > highestUid {
   37.54 +                    highestUid = cdMsg.uid
   37.55 +                }
   37.56 +            }
   37.57 +        }
   37.58 +        return Int(highestUid)
   37.59 +    }
   37.60 +
   37.61 +    func nextUid() -> Int {
   37.62 +        return latestUid() + 1
   37.63 +    }
   37.64 +
   37.65      func testIncomingMessage(references: [Message],
   37.66                               indexPathUpdated: IndexPath?) -> Message {
   37.67          XCTAssertEqual(emailListViewModel.messages.count, topMessages.count)
   37.68          emailListViewModel.currentDisplayedMessage = displayedMessage
   37.69  
   37.70 -        let incomingMessage = createMessage(number: topMessages.count + 1)
   37.71 +        let incomingMessage = createMessage(number: nextUid())
   37.72          incomingMessage.references = references.map {
   37.73              return $0.messageID
   37.74          }
   37.75 @@ -410,35 +461,11 @@
   37.76      }
   37.77  
   37.78      class MyMessageSyncServiceProtocol: MessageSyncServiceProtocol {
   37.79 -        weak var errorDelegate: MessageSyncServiceErrorDelegate?
   37.80 -        weak var sentDelegate: MessageSyncServiceSentDelegate?
   37.81 -        weak var syncDelegate: MessageSyncServiceSyncDelegate?
   37.82 -        weak var stateDelegate: MessageSyncServiceStateDelegate?
   37.83 -        weak var flagsUploadDelegate: MessageSyncFlagsUploadDelegate?
   37.84 -
   37.85          func requestVerification(account: Account, delegate: AccountVerificationServiceDelegate) {
   37.86          }
   37.87  
   37.88          func requestFetchOlderMessages(inFolder folder: Folder) {
   37.89          }
   37.90 -
   37.91 -        func requestDraft(message: Message) {
   37.92 -        }
   37.93 -
   37.94 -        func requestSend(message: Message) {
   37.95 -        }
   37.96 -
   37.97 -        func requestFlagChange(message: Message) {
   37.98 -        }
   37.99 -
  37.100 -        func requestMessageSync(folder: Folder) {
  37.101 -        }
  37.102 -
  37.103 -        func start(account: Account) {
  37.104 -        }
  37.105 -
  37.106 -        func cancel(account: Account) {
  37.107 -        }
  37.108      }
  37.109  
  37.110      class MyEmailListViewModelDelegate: EmailListViewModelDelegate {
    38.1 --- a/pEpForiOSTests/Models/LoginViewModelTests.swift	Wed Jul 04 11:24:51 2018 +0200
    38.2 +++ b/pEpForiOSTests/Models/LoginViewModelTests.swift	Wed Jul 04 11:27:58 2018 +0200
    38.3 @@ -25,12 +25,6 @@
    38.4  
    38.5  class LoginViewModelTests: CoreDataDrivenTestBase {
    38.6      class TestMessageSyncService: MessageSyncServiceProtocol {
    38.7 -        weak var errorDelegate: MessageSyncServiceErrorDelegate?
    38.8 -        weak var sentDelegate: MessageSyncServiceSentDelegate?
    38.9 -        weak var syncDelegate: MessageSyncServiceSyncDelegate?
   38.10 -        weak var stateDelegate: MessageSyncServiceStateDelegate?
   38.11 -        weak var flagsUploadDelegate: MessageSyncFlagsUploadDelegate?
   38.12 -
   38.13          let accountSettings: TestDataBase.AccountSettings
   38.14          let expLookedUp: XCTestExpectation
   38.15  
    39.1 --- a/pEpForiOSTests/NetworkServiceTests.swift	Wed Jul 04 11:24:51 2018 +0200
    39.2 +++ b/pEpForiOSTests/NetworkServiceTests.swift	Wed Jul 04 11:27:58 2018 +0200
    39.3 @@ -10,6 +10,7 @@
    39.4  
    39.5  import MessageModel
    39.6  import pEpForiOS
    39.7 +@testable import pEpForiOS
    39.8  
    39.9  class NetworkServiceTests: XCTestCase {
   39.10      var persistenceSetup: PersistentSetup!
   39.11 @@ -178,7 +179,7 @@
   39.12          }
   39.13          XCTAssertFalse(modelDelegate.hasChangedMessages)
   39.14  
   39.15 -        TestUtil.cancelNetworkService(networkService: networkService, testCase: self)
   39.16 +        TestUtil.cancelNetworkServiceAndWait(networkService: networkService, testCase: self)
   39.17      }
   39.18  
   39.19      func testCancelSyncImmediately() {
   39.20 @@ -194,7 +195,7 @@
   39.21  
   39.22          for _ in 0...10 {
   39.23              networkService.start()
   39.24 -            TestUtil.cancelNetworkService(networkService: networkService, testCase: self)
   39.25 +            TestUtil.cancelNetworkServiceAndWait(networkService: networkService, testCase: self)
   39.26          }
   39.27  
   39.28          XCTAssertNil(CdFolder.all())
   39.29 @@ -233,31 +234,12 @@
   39.30          let modelDelegate = MessageModelObserver()
   39.31          MessageModelConfig.messageFolderDelegate = modelDelegate
   39.32  
   39.33 -        let sendLayerDelegate = SendLayerObserver()
   39.34 -
   39.35 -        let networkService = NetworkService(parentName: #function)
   39.36 -        networkService.sleepTimeInSeconds = 2
   39.37 -
   39.38 -        // A temp variable is necassary, since the networkServiceUnitTestDelegate is weak
   39.39 -        let expAccountsSynced = expectation(description: "expSingleAccountSynced1")
   39.40 -        var del = NetworkServiceObserver(
   39.41 -            expAccountsSynced: expAccountsSynced,
   39.42 -            failOnError: useCorrectSmtpAccount)
   39.43 -
   39.44 -        networkService.unitTestDelegate = del
   39.45 -        networkService.sendLayerDelegate = sendLayerDelegate
   39.46 -
   39.47          let cdAccount = useCorrectSmtpAccount ? SecretTestData().createWorkingCdAccount() :
   39.48              SecretTestData().createSmtpTimeOutCdAccount()
   39.49          TestUtil.skipValidation()
   39.50          Record.saveAndWait()
   39.51  
   39.52 -        networkService.start()
   39.53 -
   39.54 -        // Wait for first sync, mainly to have folders
   39.55 -        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   39.56 -            XCTAssertNil(error)
   39.57 -        })
   39.58 +        TestUtil.syncAndWait(testCase: self, skipValidation: true)
   39.59  
   39.60          let from = CdIdentity.create()
   39.61          from.userName = cdAccount.identity?.userName ?? "Unit 004"
   39.62 @@ -288,15 +270,7 @@
   39.63              XCTAssertEqual(m.sendStatus, SendStatus.none)
   39.64          }
   39.65  
   39.66 -        let expAccountsSynced2 = expectation(description: "expSingleAccountSynced2")
   39.67 -        del = NetworkServiceObserver(
   39.68 -            expAccountsSynced: expAccountsSynced2)
   39.69 -        networkService.unitTestDelegate = del
   39.70 -
   39.71 -        // Wait for next sync, to verify outgoing mails
   39.72 -        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   39.73 -            XCTAssertNil(error)
   39.74 -        })
   39.75 +        TestUtil.syncAndWait(testCase: self, skipValidation: true)
   39.76  
   39.77          // Check that the sent mails have been deleted
   39.78          Record.refreshRegisteredObjects(mergeChanges: true)
   39.79 @@ -306,55 +280,37 @@
   39.80              }
   39.81          }
   39.82  
   39.83 -        // Make sure the sent folder will still *not* be synced in the next step
   39.84 -        sentFolder.lastLookedAt = Date(
   39.85 -            timeIntervalSinceNow: -(networkService.timeIntervalForInterestingFolders + 1))
   39.86 -        Record.saveAndWait()
   39.87 -
   39.88          // Will the sent folder be synced on next sync?
   39.89 -        let accountInfo = AccountConnectInfo(accountID: cdAccount.objectID)
   39.90 -        var fis = networkService.currentWorker?.determineInterestingFolders(
   39.91 -            accountInfo: accountInfo) ?? []
   39.92 +        var fis = TestUtil.determineInterestingFolders(in: cdAccount)
   39.93          XCTAssertEqual(fis.count, 1) // still only inbox
   39.94 -
   39.95 -        var haveSentFolder = false
   39.96 +        var isSentFolderInteresting = false
   39.97          for f in fis {
   39.98              if f.folderType == .sent {
   39.99 -                haveSentFolder = true
  39.100 +                isSentFolderInteresting = true
  39.101              }
  39.102          }
  39.103 -        XCTAssertFalse(haveSentFolder)
  39.104 +        XCTAssertFalse(isSentFolderInteresting)
  39.105  
  39.106          // Make sure the sent folder will be synced in the next step
  39.107          sentFolder.lastLookedAt = Date()
  39.108          Record.saveAndWait()
  39.109  
  39.110 -        // Will the sent folder be synced on next sync?
  39.111 -        fis = networkService.currentWorker?.determineInterestingFolders(
  39.112 -            accountInfo: accountInfo) ?? []
  39.113 +        fis = TestUtil.determineInterestingFolders(in: cdAccount)
  39.114          XCTAssertGreaterThan(fis.count, 1)
  39.115  
  39.116          for f in fis {
  39.117              if f.folderType == .sent {
  39.118 -                haveSentFolder = true
  39.119 +                isSentFolderInteresting = true
  39.120              }
  39.121          }
  39.122 -        XCTAssertTrue(haveSentFolder)
  39.123 +        XCTAssertTrue(isSentFolderInteresting)
  39.124  
  39.125 -        del = NetworkServiceObserver(
  39.126 -            expAccountsSynced: expectation(description: "expSingleAccountSynced3"))
  39.127 -        networkService.unitTestDelegate = del
  39.128 -        
  39.129 -        // Wait for next sync
  39.130 -        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
  39.131 -            Log.info(component: "didSync", content: "expSingleAccountSynced3 timeout?")
  39.132 -            XCTAssertNil(error)
  39.133 -        })
  39.134 +        // sync
  39.135 +        TestUtil.syncAndWait(testCase: self, skipValidation: true)
  39.136  
  39.137          if useCorrectSmtpAccount {
  39.138              // those messages do not exist if we are using an incorrect account
  39.139              TestUtil.checkForExistanceAndUniqueness(uuids: outgoingMessageIDs)
  39.140          }
  39.141 -        TestUtil.cancelNetworkService(networkService: networkService, testCase: self)
  39.142      }
  39.143  }
    40.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    40.2 +++ b/pEpForiOSTests/Resources/unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_pub.asc	Wed Jul 04 11:27:58 2018 +0200
    40.3 @@ -0,0 +1,52 @@
    40.4 +-----BEGIN PGP PUBLIC KEY BLOCK-----
    40.5 +
    40.6 +mQINBFs076YBEAC3lK5FSUgOejCL1pSuky5Od2uifT0Jsgnoo/Ub9VbHOTCco5Nm
    40.7 +yvsnqltPzFLaqVDJica8XotHkY2Opf1sIfIeGaY80UGTHgWV5u4KB9UI5TAao2zr
    40.8 +9U6naDUnK4g05Gnat5neG8EIS6QLsPmi6icbEnd5gjeg7Yh7kvDK8TA+i7brfCoi
    40.9 +F1Es61u6VvBLypZLviD23aUB7DcMJ6rhw7GtyTqCGyoZCGn4E+hFVaNGTz+5Gzid
   40.10 +VKK9b827LVJkBbhLscJrdN+vEtS3pcFZASwCyEkotrsT0J21ftTVrSGD2cBceYZE
   40.11 +9JoZlMwAWRW6x6uKJ/eBOuakkj9yr8qvQPL4Xvny+lTtSxYmE75fvtQ8U/Hzw9Ja
   40.12 +W9DT+W6jqlgt27zCktfdr5XFutdcp6NKQ95R/49mCSoXX7GIpS/pFkW0J0SKP0FE
   40.13 +TG4UrIPNbk/5aQJfwvyDVCS6Q5Q+QIRLvVD5m0zH297qOKyaGRUpWhjiPDEVJIch
   40.14 +b1h/mCV0O6IC26OyIwnuA3Lg0IJ8NGadIFaAcfncdC7ij/pSnKyga4V7RK4TMLVY
   40.15 +rXIfLNV1XRPeDZyVsmPYyY2F2YBjFTxx/zNtOloT8Bdv/jHVBYf0E2LvXjh7aDVo
   40.16 +P9orz8ibg0XXopoMY1itMi6mzKhPnfmhJz9rb10azOxTAWnNET0v8yWYNQARAQAB
   40.17 +tCp1bml0dGVzdC5pb3MuMyA8dW5pdHRlc3QuaW9zLjNAcGVwdGVzdC5jaD6JAlQE
   40.18 +EwEKAD4WIQRVCp5iaCIEDlfLFRplHEpdsVt3owUCWzTvpgIbAwUJB4YfgAULCQgH
   40.19 +AwUVCgkICwUWAgMBAAIeAQIXgAAKCRBlHEpdsVt3o6BaEACFWMVK/psVWMaa52QH
   40.20 +smL2fMWgxgtHp94ZoW3EK2j9iuwynCDQ6MiDfQdDeLxCLgTqqkH6/vXLEE9RJ5MM
   40.21 +3APNBfoucDoZxkFr3z5PQg2yu8MxmeyojT/EbTByN2C1WJ69xX91Z/m+PouKw7Ez
   40.22 +J2pyjj9j1CZfNGNJbTNEYNA0D7xPG47kPJqPz2dsW5cMNdFtaWHSiEUUJ3jyL10n
   40.23 +hd4aJvxMqoJ9I4xu1v/mJwip4sjx97RDnWN8IRv4AOxFeZ/4IYE0SLNGy3Ri8o/P
   40.24 +YbTzRFk971mb2RVhQbqPAgNuOzCblWl1Fu8v5w5dxtnnfx9PzaVg/K5ojSjuxEsd
   40.25 +GhvM5stPDEnjx37a7Oy0cEeuSukQTjLAX5cNcssN5Eh38PA4XOBeRDhIWo5dcJW9
   40.26 +mHeqDwnegXF4x0x3ydwvlDB6H9apNXVM2PDHRqNkstEffYMogIzaYF3mrjnub1Id
   40.27 +LxTAGixjzj9SBjpEaK6IUa7BrtlI04zgpjA/nPuKkeqQCpWzKGzIjMjya+oVUBsz
   40.28 +j0fEz4GbeQCy9eqi1ZuikHxh7Tn7NvlanZEofaF6gEX3EfLl3JHDXAJQT7xb5d2T
   40.29 +NJ5y43V/ytir12sTBVbdrsIKP3bqPJBKeX8b8KohbB7VG66DNf42b0APstsyEoAi
   40.30 +qCV6VGZBWx4cGhvgNPTppcfqC7kCDQRbNO+mARAA29yOf2di4YwcQjHJw5yci/Z6
   40.31 +259C9fRmGnEIUxAqqfsm3EM5uiWZEw8huI7DECqe+0qKmfh7sT+MA7ShDr/jQdjw
   40.32 +NsQ0BVpzkRj2t371OP2e+sJrGlyjSMd+eam1PbSEZ0WcN8Ly7FuwX+EPE/HAwz/2
   40.33 +LsyeSDeWsGzqlYWBRuTx7dgJuurZbVIqRmcAZAKB++AAb/RoZwDRHcu7GMaSxQLm
   40.34 +d767a6mP+XrQnEwIWITZ9rbLh/71A86tjMlILpfT6uLhXJ3Y4/sqmfESvqQPYRy+
   40.35 +Gyi7uM2KP28rbBPTPAyBubNi635IHiUAaOopsMb8M3agyjhVgtjyKx/GxNlC9tky
   40.36 +r2qS2JOevAD8GcrYE2UH+VedEeraxCBRe+N6/tGrJlZ//FeRYgnmt8ihw2eP4Kwk
   40.37 +1MfdO2fVBL5Ar43OTBb4s/CusExEi4OzPIo265hzNqk6IoF6mROXUttNUdCwyAM0
   40.38 +Me8PjXl9cUqsqHk5YU0Z2ImU/Eql9q/p3CEQ48TeLrxrvoyedr9ocRTaFyFkUYTH
   40.39 +WfxKMyEfjdPCU4sPZ5o8HaRvP1LA6t0bNUvVadWhKkU4XKUYrNghBHJjsgwdYth2
   40.40 +uaBmcPjnYbJ95LzTvl1jI1yd3hDLLDjX/Q2fWGnM1JGIaYG+nL+VroBJ2dA4M8rv
   40.41 +ytXNhvvuvhVpnRlguwMAEQEAAYkCPAQYAQoAJhYhBFUKnmJoIgQOV8sVGmUcSl2x
   40.42 +W3ejBQJbNO+mAhsMBQkHhh+AAAoJEGUcSl2xW3ejwyIQAJT3aV3SuoViZVGbJArw
   40.43 +PUKc7qeZcqHwb3tHZDjtXZfYvWUJ/jiQ7S1Rppd8Q79/ZeOk8PanAoYiW5xrywJA
   40.44 +x+vzqqud2n4U5PzD6uMSIpZlu3SZogfBllvi6f5PL/+10/YfiJS/8WhKDQ4GFRfJ
   40.45 +yhGRfxK6q2HHO1qoGKZxsXmnDgQxrJNKjOIHCfYL8Gg9GZSUDb0d2KrsHC2R3aAt
   40.46 +bfelqgA/ZJT7YH36ggdhHjFoeZ6MmszntBiqY/cOKI1SC6uUu928SB7WC0KsBNyW
   40.47 +4O4Oz3ovokpUowubJ6UnbKHuTN3FMxHL5rmnfk33G7DKLYoc27laU9aUEa1IUOgz
   40.48 +3zVzJVj1wWMDXy/YQ7uFAB4uoy2c3kgIyLgoBPq+gNuIty4X5BxYvg+grp7o5Xdf
   40.49 +4mQWt3noUvWCxzupYXUcBTipQMlBWS5IXSGZlukxiI+b62cQmi+Ooy7DaqYNX5aZ
   40.50 +OE1Ahyu9uaBBVihn5ZF7wPxnTEqdydp39mt/UfULNZlJvKjFItj3WGARrivDH75/
   40.51 +Zm4RfOK6KAlBHN1Et6CCey8qTsQIXBd4PYOM56twnnjreg+4Od4zkg0B1o62bob5
   40.52 +Cji5C1ju6flzZzTLcvz6eFSzMdvxTQICVJWyOxXTx8AhPF4swBF9aq69tcbLIEl9
   40.53 +wjZnEtx2jqStFATUHxQ+vUte
   40.54 +=qnIt
   40.55 +-----END PGP PUBLIC KEY BLOCK-----
    41.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    41.2 +++ b/pEpForiOSTests/Resources/unittest_ios_3_peptest_ch_550A_9E62_6822_040E_57CB_151A_651C_4A5D_B15B_77A3_sec.asc	Wed Jul 04 11:27:58 2018 +0200
    41.3 @@ -0,0 +1,106 @@
    41.4 +-----BEGIN PGP PRIVATE KEY BLOCK-----
    41.5 +
    41.6 +lQcYBFs076YBEAC3lK5FSUgOejCL1pSuky5Od2uifT0Jsgnoo/Ub9VbHOTCco5Nm
    41.7 +yvsnqltPzFLaqVDJica8XotHkY2Opf1sIfIeGaY80UGTHgWV5u4KB9UI5TAao2zr
    41.8 +9U6naDUnK4g05Gnat5neG8EIS6QLsPmi6icbEnd5gjeg7Yh7kvDK8TA+i7brfCoi
    41.9 +F1Es61u6VvBLypZLviD23aUB7DcMJ6rhw7GtyTqCGyoZCGn4E+hFVaNGTz+5Gzid
   41.10 +VKK9b827LVJkBbhLscJrdN+vEtS3pcFZASwCyEkotrsT0J21ftTVrSGD2cBceYZE
   41.11 +9JoZlMwAWRW6x6uKJ/eBOuakkj9yr8qvQPL4Xvny+lTtSxYmE75fvtQ8U/Hzw9Ja
   41.12 +W9DT+W6jqlgt27zCktfdr5XFutdcp6NKQ95R/49mCSoXX7GIpS/pFkW0J0SKP0FE
   41.13 +TG4UrIPNbk/5aQJfwvyDVCS6Q5Q+QIRLvVD5m0zH297qOKyaGRUpWhjiPDEVJIch
   41.14 +b1h/mCV0O6IC26OyIwnuA3Lg0IJ8NGadIFaAcfncdC7ij/pSnKyga4V7RK4TMLVY
   41.15 +rXIfLNV1XRPeDZyVsmPYyY2F2YBjFTxx/zNtOloT8Bdv/jHVBYf0E2LvXjh7aDVo
   41.16 +P9orz8ibg0XXopoMY1itMi6mzKhPnfmhJz9rb10azOxTAWnNET0v8yWYNQARAQAB
   41.17 +AA//VZPPK0hOeUU3lTBp7fl5SCW+PKhiCD+BA/xOWgKjucosoZBVOcFck/CDkW7f
   41.18 +nsP3/4Mk7BGsnqe40+sH+FzYp3RwfIBVr7V6iBb3FyDwi61/H6PYNiSLzlkzEffm
   41.19 +jjV+LPGSninU8b+FENgmfu3lXebdX/y2XuPr0CM+8XO+eCGcGykt4Od6tVRf1b0K
   41.20 +VmJxboyKhFntPji1ropdS9H0ZV1dU2JDXRkBgR4SlCOoMbHmtCz+1sSp+mIAhwVJ
   41.21 +F4xdDZgnV/1K+qtuEtAHkiyMLh3ckMih68o/gmhF4ffLnFGpLNKPp1xY65zj/jOJ
   41.22 +ACFtE0GvTIQ/GS99uUI+WzUNLbWbF/jUAflSrEUxIbyeghanBcS/zssMKppB25pY
   41.23 +1xD/+R+vMULUYodwJ3k/EpZXNXNOgt4Yyd99h+y/MbLxF2kiI0maT9EO+mK2nmKM
   41.24 +tW8/EXBzzKcjUXgJ0snRtQKn+I+5EKAlAGQrdf8qSPtEWRdzR3xoE/aq6cxMDU2Q
   41.25 +M01ce+YncsWURqbKnlzA4lu4kB8NewyZhDxlkROo/P77/hP0gOCWidUxC+nc309h
   41.26 +/1T8mqh+0bulPtEM03FPFNlzitC84h8CbTFI4y/bocLsZuvl5W+VCBSIsdtUGd4i
   41.27 +uVaYI7Q0n2OwruB0WUp4DFm+8mf7jlxKitdcQxAMMdJ4a18IANAQ1XREr9aTjKr7
   41.28 +qYuCTsP1ns/8NeJeYpu6MaEEBx+4n6iWZC/BI6ziHVxWV3mBAuuh7jdVLeMlvN4d
   41.29 +/4FhpFeJwAIr8pJltetysGerMKWGWCKpC4+y4zrXjxGAcc4GEEO1/EVTrBNHnzaa
   41.30 +2vasfqwri7R9KZUP37Ao8lqV9G93Ge0pApwwjIVVRTbJ/HOCWX4R4+IDrZjdmKmA
   41.31 +PwuvQUndU7Rks/sIME3lMO1cXrsioeTqvfoGThEVlzMrSgKCTBrGAzokcZDn+g8f
   41.32 +L99Am1qhovd05hi7s0DA39IwQNJoVyPJWKYeLi3nHJWuaqEXUpF4PuSe874H1JXL
   41.33 +U8A9Ba8IAOHfych65ZjFgVCdnbApNb0O9dVvTcqD+L0U2XBH02A2pbs7LYWIG5jy
   41.34 +x7spygQn6FxoMZkUarfS7k2lFDyVpI85K9q4CMzmqNt2Y1VTuYAnllr0K5oPon3C
   41.35 +a+yBpqg4sll/n3JqEmp1yKo0431ifVkkBsYDRRF+VoQkD+/botltxdpSDX7ScX7y
   41.36 +kJ6edSeqXEInWETnh9tQjM6oqjFrFbWP0f/e6Y3ZvrbYF0BqbbzfTmOdOiV3DMJ7
   41.37 +texccf7CJWPxARaCwIVlVhtLHlaH5fIk/mENmm+t+eSZOMaYCt4bz0fcfiLxKh+V
   41.38 +n5/vnhBP8NJHvCvM660fpI7oiXDcXVsH/1Bl/PLH8EMSbsC37cHguWKge0rG8tia
   41.39 +DjZrQyLDADhEetMkv0MdPIpO7ytV4xK7NKv5QyuC2M2c41tU0UiK9uSOCcMn0SIi
   41.40 +IaXj/TyaeNisEu4luFW9hRDyy8NceWK9jZVglBZqGQFuNxnbWsvyOYs07NatqVix
   41.41 +06ZwdZvWFMqLSdWQhBEb0ZH53R/AxcZSe6KOJBJrI6FC8yHxoiWKaCyuObtmc07l
   41.42 +jHw7fRDygZzhPHyQfpx9k4scTZ5IdJrnOr2RnMt5amNaJRz3or79gKX/98x7RiEq
   41.43 +farCarNTmzZT3d2KF2hNk8vd3EQRiEjVd0hzNwhLB+Hg0fa05ckhseKJJrQqdW5p
   41.44 +dHRlc3QuaW9zLjMgPHVuaXR0ZXN0Lmlvcy4zQHBlcHRlc3QuY2g+iQJUBBMBCgA+
   41.45 +FiEEVQqeYmgiBA5XyxUaZRxKXbFbd6MFAls076YCGwMFCQeGH4AFCwkIBwMFFQoJ
   41.46 +CAsFFgIDAQACHgECF4AACgkQZRxKXbFbd6OgWhAAhVjFSv6bFVjGmudkB7Ji9nzF
   41.47 +oMYLR6feGaFtxCto/YrsMpwg0OjIg30HQ3i8Qi4E6qpB+v71yxBPUSeTDNwDzQX6
   41.48 +LnA6GcZBa98+T0INsrvDMZnsqI0/xG0wcjdgtVievcV/dWf5vj6LisOxMydqco4/
   41.49 +Y9QmXzRjSW0zRGDQNA+8TxuO5Dyaj89nbFuXDDXRbWlh0ohFFCd48i9dJ4XeGib8
   41.50 +TKqCfSOMbtb/5icIqeLI8fe0Q51jfCEb+ADsRXmf+CGBNEizRst0YvKPz2G080RZ
   41.51 +Pe9Zm9kVYUG6jwIDbjswm5VpdRbvL+cOXcbZ538fT82lYPyuaI0o7sRLHRobzObL
   41.52 +TwxJ48d+2uzstHBHrkrpEE4ywF+XDXLLDeRId/DwOFzgXkQ4SFqOXXCVvZh3qg8J
   41.53 +3oFxeMdMd8ncL5Qweh/WqTV1TNjwx0ajZLLRH32DKICM2mBd5q457m9SHS8UwBos
   41.54 +Y84/UgY6RGiuiFGuwa7ZSNOM4KYwP5z7ipHqkAqVsyhsyIzI8mvqFVAbM49HxM+B
   41.55 +m3kAsvXqotWbopB8Ye05+zb5Wp2RKH2heoBF9xHy5dyRw1wCUE+8W+XdkzSecuN1
   41.56 +f8rYq9drEwVW3a7CCj926jyQSnl/G/CqIWwe1RuugzX+Nm9AD7LbMhKAIqglelRm
   41.57 +QVseHBob4DT06aXH6gudBxgEWzTvpgEQANvcjn9nYuGMHEIxycOcnIv2etufQvX0
   41.58 +ZhpxCFMQKqn7JtxDObolmRMPIbiOwxAqnvtKipn4e7E/jAO0oQ6/40HY8DbENAVa
   41.59 +c5EY9rd+9Tj9nvrCaxpco0jHfnmptT20hGdFnDfC8uxbsF/hDxPxwMM/9i7Mnkg3
   41.60 +lrBs6pWFgUbk8e3YCbrq2W1SKkZnAGQCgfvgAG/0aGcA0R3LuxjGksUC5ne+u2up
   41.61 +j/l60JxMCFiE2fa2y4f+9QPOrYzJSC6X0+ri4Vyd2OP7KpnxEr6kD2Ecvhsou7jN
   41.62 +ij9vK2wT0zwMgbmzYut+SB4lAGjqKbDG/DN2oMo4VYLY8isfxsTZQvbZMq9qktiT
   41.63 +nrwA/BnK2BNlB/lXnRHq2sQgUXvjev7RqyZWf/xXkWIJ5rfIocNnj+CsJNTH3Ttn
   41.64 +1QS+QK+NzkwW+LPwrrBMRIuDszyKNuuYczapOiKBepkTl1LbTVHQsMgDNDHvD415
   41.65 +fXFKrKh5OWFNGdiJlPxKpfav6dwhEOPE3i68a76Mnna/aHEU2hchZFGEx1n8SjMh
   41.66 +H43TwlOLD2eaPB2kbz9SwOrdGzVL1WnVoSpFOFylGKzYIQRyY7IMHWLYdrmgZnD4
   41.67 +52GyfeS8075dYyNcnd4Qyyw41/0Nn1hpzNSRiGmBvpy/la6ASdnQODPK78rVzYb7
   41.68 +7r4VaZ0ZYLsDABEBAAEAD/wKnU9T303qzbTfjT/Mbdcw+qlbyQiWj0IOrL5WJEWr
   41.69 +BzENGUHENuR5AxYBrwLD2Qrr3uFqJZDzMbo0w5ey+piiKTMPsdFBIOjLbIUcvshg
   41.70 +A/6U5KX/E9HeRaNZhFP3sqthwP6VwpevyY1uU5NCxz0jMAdWoxJRMj3YLqn0pglj
   41.71 +vtHfRbrSh1wP5xObaoWeTnCwFFRxEz+nGWb/JULXI18am9U8mry5qeoFhWRk51bP
   41.72 +zzomqxSQcEKr+GJwlZciiLn2cRWoPwalmw6nd/gLtCf57EVRUlodxe6SuW9DPIHK
   41.73 +OGBa8sonzGT275phdkyk+DZc1bFVkfqu4oJuio4yGrsc0N808i9yqBfl+i4nUcwc
   41.74 +d3N55bBBYjZSmcdCYWGtHU/Hkkr5y18GJ9AXu2fzJVjcSLQk+eSI29piu2T3C9bZ
   41.75 +weCF5UOCWTU90rCudteQCabEKV4p1vagRKe8HZi3fP7CTQtNXu0MZ3sRZyKBGWgg
   41.76 +QLcIHpSeLqJEkZE9dIoDvJnfl42IHdp+Lq7uJ1w4TOJTepxRuxGxYBKolEDwAMQB
   41.77 +OtkjX2NzzD4kmxxpKqjupwagC74xkZsygWymV03l9BcLOMH6RlkPWdBbvUBMNY42
   41.78 +detcorcgvIjlxSB4iWgZRar4lVW/hOEQfEPf7IwrWPEZxvB2APRnbNte0ODh2xMI
   41.79 +QQgA7Flu4fcwkc3259ewfr6uJpE4JzKG7yAvj6C7Qd0I5v0R9XaXAlQZO8dzpmyx
   41.80 +DIvKq8+yqRgl2VEbP4ChOQzekHDmmJgOsRety2A4Ev4J9Jo3YxylF+h+WjoQrT6Q
   41.81 +HRbwigxSIHKQ3PLI8VTDEInxpSre0vxjTC3k4e2DYMm3R9yTccje+cMstFUC4o5+
   41.82 +clhEhDsKfifU4WM/3ISJqsE7/a2UHKNFZKjUkXsycY7suUAQYSPwkgL9V5c6yVFr
   41.83 +6kB49avKGbBURmxnQHVZm46q9+ueyKz7MiMSSBURYh07YajW0Uh5V32mNWBjDWN5
   41.84 +0DE3AqclubL7AycKYbgH4soWmQgA7iQwkAUYWP3pgF+EKdbdrIGmO5HHTfUX9IuF
   41.85 +WmlULZnxQhWv53V5cga0zR2QJZ1cGtYhTt7Auz0mMhJjD1fKrNv3mLPzwMAzfYrp
   41.86 +9zhTI8hYQ6ld7ZQbwM7LS8PoZmR/dXdLZjSBQu+P5c+f1VRb2Imbuhiyn0g8NwXP
   41.87 +tP5VDjDXsm8eFgsvaNd9OOZTAvsxLJRfuWg9GD+maGSaz3mzT1aw+NexdYrpNTYo
   41.88 +eF2vR1H3EdsIFe98o+IpI2Nn9CYctq41ut8TufKZi+vx5dj6dx6iL07JVFRqXP/0
   41.89 +QaBRpHCDEPirBFcvEcjcFQlWM3Mv6hXBzJ3qDJ6gQ2opHuQL+wgAo73PcWu4Oks7
   41.90 +WMQq/xDsGPTqAsNct4CIgpdyoqh2qm4STmYzUSg9fnqicpnwbD3KLknIScXLyDU+
   41.91 +RQ5VbYwd7917vXanD1N59Pdf/S1aEY/5ggM/eTXrijmZC7NsdI7QqtAdAvz2CWdz
   41.92 +r5A70upl/qV+eWmEykhULJvcJ4qi/m06JZ2E/oDW+2PAhswpEuAVHDRSR2IEkOu6
   41.93 +IFLJYYON6R8ghESzrUskr17k5FLWH6Isetoo0rhvpBTRDmeDmjAyhFqKBn5G9Ozk
   41.94 +gle1g4UBVntSIxLEF4ivvG+hMIHwQ9Nq8LddgmAFGb9hwnOF86nSzfInmBGExEsL
   41.95 +E0hZrTDILnnNiQI8BBgBCgAmFiEEVQqeYmgiBA5XyxUaZRxKXbFbd6MFAls076YC
   41.96 +GwwFCQeGH4AACgkQZRxKXbFbd6PDIhAAlPdpXdK6hWJlUZskCvA9Qpzup5lyofBv
   41.97 +e0dkOO1dl9i9ZQn+OJDtLVGml3xDv39l46Tw9qcChiJbnGvLAkDH6/Oqq53afhTk
   41.98 +/MPq4xIilmW7dJmiB8GWW+Lp/k8v/7XT9h+IlL/xaEoNDgYVF8nKEZF/ErqrYcc7
   41.99 +WqgYpnGxeacOBDGsk0qM4gcJ9gvwaD0ZlJQNvR3YquwcLZHdoC1t96WqAD9klPtg
  41.100 +ffqCB2EeMWh5noyazOe0GKpj9w4ojVILq5S73bxIHtYLQqwE3Jbg7g7Pei+iSlSj
  41.101 +C5snpSdsoe5M3cUzEcvmuad+TfcbsMotihzbuVpT1pQRrUhQ6DPfNXMlWPXBYwNf
  41.102 +L9hDu4UAHi6jLZzeSAjIuCgE+r6A24i3LhfkHFi+D6Cunujld1/iZBa3eehS9YLH
  41.103 +O6lhdRwFOKlAyUFZLkhdIZmW6TGIj5vrZxCaL46jLsNqpg1flpk4TUCHK725oEFW
  41.104 +KGflkXvA/GdMSp3J2nf2a39R9Qs1mUm8qMUi2PdYYBGuK8Mfvn9mbhF84rooCUEc
  41.105 +3US3oIJ7LypOxAhcF3g9g4znq3CeeOt6D7g53jOSDQHWjrZuhvkKOLkLWO7p+XNn
  41.106 +NMty/Pp4VLMx2/FNAgJUlbI7FdPHwCE8XizAEX1qrr21xssgSX3CNmcS3HaOpK0U
  41.107 +BNQfFD69S14=
  41.108 +=zoDk
  41.109 +-----END PGP PRIVATE KEY BLOCK-----
    42.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    42.2 +++ b/pEpForiOSTests/Resources/unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_pub.asc	Wed Jul 04 11:27:58 2018 +0200
    42.3 @@ -0,0 +1,52 @@
    42.4 +-----BEGIN PGP PUBLIC KEY BLOCK-----
    42.5 +
    42.6 +mQINBFs07/MBEADlV1e+WGv+8zj/sGZ3MpZONNX4LR2bclVIm5PrI/8puY5XMtiV
    42.7 +pHXcRMUHPmYKn8/+WT4VHlFRlIiZSM8QeHupNvAlWo5SStJljEusXb7tN2N7voeH
    42.8 +RjJwY0PtGIAMvv/5+za7J2dNldGy7vad7dc63gUfbTGkBsnymEmWjCW/h6l59VWD
    42.9 +EAaQPPMlD/zLXSwRipN7fHX7HJ8UwYRxGD7cw+SVEL8n9UlsClx9UhmhBOY7rm9V
   42.10 +YnSddxXnv8uiAbn6dLqMocZ3FadcKNTi/FZiYsN3Vse2sn0U2AGrMHoTNLprLZ6I
   42.11 +WQJVliBKYkmDny9zAcCq711Qt//9pAiJ+MZQGqFXAM+BX+WUeOlWq1Kx1ftx+i7M
   42.12 +8ARXKiVq9Z+HxNw5B3fch3EdMRHz/Z1JcUyEE8TyztGecIsaRMHxEmpQV22+80sU
   42.13 +mlv4NfVrqYwAbKWtqG2nyE4wsmzi6quSBIfNRcG41IxXbcTFf1+0767mZKwlWZlt
   42.14 +PDc9tv6iwMRG19BbXk4QIgLuGdBAI8VJy+hYtUe5y3iLZOqRhV3DL1aa0uFs72wv
   42.15 +iX1YswBG7wY8PhOs6g7KPoL+2VR9rNUvlx1u45oVy8qcpRhHy6YhwF3j8LHcVnHI
   42.16 +8tttv39SK1fqBRPp5S97oHvTyENKSixWHht41bQ+PECz1tUHTZqGQ+OAOwARAQAB
   42.17 +tCp1bml0dGVzdC5pb3MuNCA8dW5pdHRlc3QuaW9zLjRAcGVwdGVzdC5jaD6JAlQE
   42.18 +EwEKAD4WIQRmr1gEqHkeAbQHElrK8Ng4FUJJxAUCWzTv8wIbAwUJB4YfgAULCQgH
   42.19 +AwUVCgkICwUWAgMBAAIeAQIXgAAKCRDK8Ng4FUJJxPImD/9m8gWObqShfJD9U5wq
   42.20 +bH0JCfM3+w3pqpkwSd9q5kmnSb77bS3fZbRuJ+qricmhOZ1kn2XlO16PEzBp8XyP
   42.21 +gSYdZE8nAFNXaZuHVhbybJKDVN4GtRzNxb8mAnoDU1QnmGbPCmH/sILo3HyyRup0
   42.22 +mLNCwYJbTp/QcbdICHyhqYU5u7YrP9kJkdTW5ShvOPe9wPPDWrvLe/iQK72J8TQa
   42.23 +b71EGzDwePPAYL1PCaj3FlK+Re0tKtneS6s/nFBztEWnSTrkMnbaOa+PLY9/uAzJ
   42.24 +X4Gm18DV3lHPA0DD5H6Zav+9+wl6FgvX8YIphSJFK75DBPSWqOJNX8LUfbCVEg0t
   42.25 +iFJad+0uEcLieA29t5mRtE46w++04VHlB1Or5644iCoQcwgD9ntz3kcLciqd1cXc
   42.26 +m0L82VCb/mtVawoNWOM6dbnzOu8fCH9qDvQTye6zQo8UlcowiEDUrDSTNDNd9lBC
   42.27 +rGkSf4QW1CJ2mzVz72ZdxbEeCgUVURdMVoFO6kjm0+/uRhf3pOv9DFot6P08z5Zl
   42.28 +ikzczyCTXER5qaQRdJVYdjNF3dfgNl+4YpQfknwH8DzPgKeagEvQKt/AE+Hr2ixM
   42.29 +M4u+sq5M8TMhvWVHh2DKd2iMB0eE2lsTjKQ6hzvkdgRSnOpFir1DjQ9VmZdduwtL
   42.30 +LqmfB2bySU6P5rVEuAcVbOe1orkCDQRbNO/zARAApN9y73lY7K3YxbibJDSxzax+
   42.31 +gENeZlukTfCb5GdQrF8jBFC+yGynin3rBlliCMIDG7l5KrFW0PkNs8RR4Jt7Xe/P
   42.32 +pvjPFT2oFGRr87GBzLzAf2U+kjRHHPROQ7Tp9qtC7F5o92S2jWtvqrVp9jx8C88M
   42.33 +ibyh9bA0sKij28c2BQUWcr2Qp2QotDz8wMMjkDtpMKHQiFp/btGdLbKAgtiVroXO
   42.34 +Xg3VQjbMArna1gj7s1vmhtLCHiIMc5vrY5mY53ohcwyPr2pTA17vefFqHFmcRhv2
   42.35 +cyoSGIbkqkeiO/KjlJdSNy8E0DGUQ1EFu0aHxEW+Hq4qZKVv+cnT4ZxrShWfE3PT
   42.36 +GTG2hJJvb+D25TKlJSdWzZfcfGMsb7I2aHg/Y1u3HFm1nwZNPKTDHR4B+/04xi63
   42.37 +idjBTphraoHYEQU4QNkdJgPkreSHVb+OLLyr5GMkm5pN953KKWoHLptcbVKUeFJC
   42.38 +UTTeobYQAOtgxCYzAp81ULbtz1ng+GZxUuHXlVpeIF/HcXvqAbbcx3KvrBme0Emc
   42.39 +V73+QJnCzCJVswLeGAuDgF15JZ2HPDqfY80XxjWRJMxKX5dl1y3jL8pj1r0GYKXp
   42.40 +qoTcYAlVPzoYVFu96Q2Ts/GmbGYJjG7rIjvy/QlqQzs3gYUga3OfCDmVA77MjT68
   42.41 +5snOQnPdC5eQCA2T8PkAEQEAAYkCPAQYAQoAJhYhBGavWASoeR4BtAcSWsrw2DgV
   42.42 +QknEBQJbNO/zAhsMBQkHhh+AAAoJEMrw2DgVQknEa9gP/3f8nnyCo5Zwy9NEyEX2
   42.43 +/+jjZW5M6PzQTQUa+JjENOiTQNuhzVQEJqEItpJtVQ0+wqLtFZqiDFYqaBqZEG9S
   42.44 +O4Qak9qNJfnGo2j+2wZqhvJyEPdJuEhA1EqFx2LoX/Mka59pXd5sYA6AEBJyeEA7
   42.45 +n/OSfrJVAXiwc46HV7tXNJ7rEvCjQj3PSaTiqQseDwui6agIUt/sD7DwHhUepNx9
   42.46 +QSAXMDoNHfoxK4mBbHdwqSloCEuZ3OtRD/0Bbvx5zKRp3R48CMMg56O2nAOBDm+F
   42.47 +Zg5ZV8bPioKv6s6ja7UvR4qEK2mNC8zRTXXJZDWLOXpAqjvJR32LK2Gi2lRRo1fK
   42.48 +isL65Ofh2jMTsK2ZDLNDYUz792gE6/Lp9adY7xLVPH+5Ri33hdGSiDwqSfNYGV5s
   42.49 +/tt05ho/xQzr47NIKO9P5b38+RysjZ6FjH7DoSkGERJg9TVfU7l8gERWp9vJw9RG
   42.50 +KZNtNqD24hgVgKefBIuFXQWYOTHKfw7KaLOdtBCrHxE5i+cI0OfXp8yVqUvJ78Zx
   42.51 +YY18eUXSQf3HihplYJWEx6ZdW7RuyrJkBtkoqPmw73qj7FqEMza7H6rDm9AvgJfq
   42.52 +e5XpCrZquaYEyuYR7tTJCaUMKDT6unSos5DlUjeD7g7uqnGpfAzit+lni5/81IBx
   42.53 +OKoqpk/2kDXwLGtsXBZPVkJb
   42.54 +=aWkz
   42.55 +-----END PGP PUBLIC KEY BLOCK-----
    43.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    43.2 +++ b/pEpForiOSTests/Resources/unittest_ios_4_peptest_ch_66AF_5804_A879_1E01_B407_125A_CAF0_D838_1542_49C4_sec.asc	Wed Jul 04 11:27:58 2018 +0200
    43.3 @@ -0,0 +1,106 @@
    43.4 +-----BEGIN PGP PRIVATE KEY BLOCK-----
    43.5 +
    43.6 +lQcYBFs07/MBEADlV1e+WGv+8zj/sGZ3MpZONNX4LR2bclVIm5PrI/8puY5XMtiV
    43.7 +pHXcRMUHPmYKn8/+WT4VHlFRlIiZSM8QeHupNvAlWo5SStJljEusXb7tN2N7voeH
    43.8 +RjJwY0PtGIAMvv/5+za7J2dNldGy7vad7dc63gUfbTGkBsnymEmWjCW/h6l59VWD
    43.9 +EAaQPPMlD/zLXSwRipN7fHX7HJ8UwYRxGD7cw+SVEL8n9UlsClx9UhmhBOY7rm9V
   43.10 +YnSddxXnv8uiAbn6dLqMocZ3FadcKNTi/FZiYsN3Vse2sn0U2AGrMHoTNLprLZ6I
   43.11 +WQJVliBKYkmDny9zAcCq711Qt//9pAiJ+MZQGqFXAM+BX+WUeOlWq1Kx1ftx+i7M
   43.12 +8ARXKiVq9Z+HxNw5B3fch3EdMRHz/Z1JcUyEE8TyztGecIsaRMHxEmpQV22+80sU
   43.13 +mlv4NfVrqYwAbKWtqG2nyE4wsmzi6quSBIfNRcG41IxXbcTFf1+0767mZKwlWZlt
   43.14 +PDc9tv6iwMRG19BbXk4QIgLuGdBAI8VJy+hYtUe5y3iLZOqRhV3DL1aa0uFs72wv
   43.15 +iX1YswBG7wY8PhOs6g7KPoL+2VR9rNUvlx1u45oVy8qcpRhHy6YhwF3j8LHcVnHI
   43.16 +8tttv39SK1fqBRPp5S97oHvTyENKSixWHht41bQ+PECz1tUHTZqGQ+OAOwARAQAB
   43.17 +AA/+IzNbwVh7Av+78OrXX1ZBYbGUYeoy0CglVOakNoQo5vOXGGUyfw1mFa/seecm
   43.18 +7FyJJSX3dHBZ1OaybKyTM5j6igdnpqQWkiTv1CxLdSJSasCEoXql/KpWlY923o5T
   43.19 +ISGz0l+vj9xFa9AiBo/NBIJ79YocSS2OX/m2x2h+awAJzzL4XUK9pEcyarMgMsEY
   43.20 +cPJVEEAv0NVSmn9TH3FwbZdmFqZukD6oyG5Kna2dcpLGYkOJPwHQDs/0zDJ6Bz8V
   43.21 +90DCleGSvIEcIC0PqgKOYmSOsb0s+UU1lCIjNq7M2sSvCAGg11CCM7VNuWs4e5JI
   43.22 +bE5jjwhm1RW2Qexr+zdRJDQt49UntCxTjibJV0vOXaeocfbRKsJN6JGQxLoCOS20
   43.23 +TAHsg4bIBkUKBC7KwA82Uw8zBKeT6SD3/fim92aTxGdO+9/GHnMzmlHuXTNqITza
   43.24 +ijXTkvCCxxDbGanBkrBpwSfPFr37CFxf3bpOFgMrntS4IbYxGrv1H9shib6r92tK
   43.25 +iKnEewzzi7xxJ4/YZgNcexHZIs2CPIj9yQDXzfb52+tnbKhOudRfyCLsbvh9sLjw
   43.26 +VGC1gEyAJDgy3ZdGbTKFtZxvC4Aja50KHo25Gu0dg23sAgINfG1QiTjvlTmyJO4E
   43.27 +VlnKS4Hht+d+vUM0zWqGu4JU205YURHo60YTiinPymalYDEIAOYrUsseJciEhYa7
   43.28 +KvssHDerJUi+nJ7wIq3O0lkE2KgxSScTuQYOiqrV7ef/NqdHTKf+VzWwOMhNEmg3
   43.29 +vgxC9XdKYmhGvbqhqmRcQHXM+aZW0EUvFCTMJwLmPHdQGzO8w8Qee8nZUTY23O2s
   43.30 +fJPL28P1SDsYQ/Si9tFk5QJyrjfYh64HUkd5FJi+m7XmQCgDrYX6kCjZ+NKwxtdZ
   43.31 +XhT7bbe0vj7K5L/ZXLYxkqtz5DEWFAcJTeEov6baV61C3vKSo4N1PPhjQ4ViFooc
   43.32 +k6vBs8ClVOgBWmHEqFqqOKxYLEP7xyhYCCod8ufHfkG9PwWs2w8gsb9DfOaGm+9t
   43.33 +7oB8RckIAP8UOtMH2YIquIpAdRW38CmL43ItqoPKuVBpwU7RQX5uyZ4uncpRKLVy
   43.34 +FUDl3EmmPcsyxvE8GHOQDeEXUbbxprJhJeThBvKlIFhy2aLDmxZG56rRDSVFwmd/
   43.35 +NR9wFuf/RYpy847HOpajwwYq/LIjKgZQlSu99zaYGPriS9/P2qJUzMFWh0odBqb2
   43.36 ++XaWhSPTCnPcv+KkoTbdngVXDwZeiLT70LA7lOExT1z3MH58mgqM7jhX807/JEsJ
   43.37 +Kh9cW6IonFBq/jmxhPPYR1dCo0tq/tbCIH3p4+3vWFwReeVgff/JSNTizp5H+Ocm
   43.38 +WJWGLB2Jx9BOzFYNFzPuap7x+faQJ+MH/1nCFvLuvJmH4mO8CXi6B93+pfracFic
   43.39 +553dCmoW2NdMNF/tSDiSz9coJYRfsrkW8KLUhHEkXnou9jjARQ98zwyHhZljJ0Vd
   43.40 +31DNSXeRq5cDWaArZE/FSy01Vnp18Jm1/csWULiG3AsJ5nXQpScR/YN7ky1WczEK
   43.41 +jpLIA1QHLNpSRSWJd70LgzMshMG5yreQGASXLicEu8aQuRgkvZZH2meyO7EujtrE
   43.42 ++BsitgcE7X8uIN1Sacm1WzB5ABTsdchqYMCSW5r140WSOKq8cRBWehpq9EYNpdMl
   43.43 +ncY/lApGMlZAA76fEiWIhCX/Wdqvmt01x9hSQLYv8zm7VBNTPlkZbix47bQqdW5p
   43.44 +dHRlc3QuaW9zLjQgPHVuaXR0ZXN0Lmlvcy40QHBlcHRlc3QuY2g+iQJUBBMBCgA+
   43.45 +FiEEZq9YBKh5HgG0BxJayvDYOBVCScQFAls07/MCGwMFCQeGH4AFCwkIBwMFFQoJ
   43.46 +CAsFFgIDAQACHgECF4AACgkQyvDYOBVCScTyJg//ZvIFjm6koXyQ/VOcKmx9CQnz
   43.47 +N/sN6aqZMEnfauZJp0m++20t32W0bifqq4nJoTmdZJ9l5TtejxMwafF8j4EmHWRP
   43.48 +JwBTV2mbh1YW8mySg1TeBrUczcW/JgJ6A1NUJ5hmzwph/7CC6Nx8skbqdJizQsGC
   43.49 +W06f0HG3SAh8oamFObu2Kz/ZCZHU1uUobzj3vcDzw1q7y3v4kCu9ifE0Gm+9RBsw
   43.50 +8HjzwGC9Twmo9xZSvkXtLSrZ3kurP5xQc7RFp0k65DJ22jmvjy2Pf7gMyV+BptfA
   43.51 +1d5RzwNAw+R+mWr/vfsJehYL1/GCKYUiRSu+QwT0lqjiTV/C1H2wlRINLYhSWnft
   43.52 +LhHC4ngNvbeZkbROOsPvtOFR5QdTq+euOIgqEHMIA/Z7c95HC3IqndXF3JtC/NlQ
   43.53 +m/5rVWsKDVjjOnW58zrvHwh/ag70E8nus0KPFJXKMIhA1Kw0kzQzXfZQQqxpEn+E
   43.54 +FtQidps1c+9mXcWxHgoFFVEXTFaBTupI5tPv7kYX96Tr/QxaLej9PM+WZYpM3M8g
   43.55 +k1xEeamkEXSVWHYzRd3X4DZfuGKUH5J8B/A8z4CnmoBL0CrfwBPh69osTDOLvrKu
   43.56 +TPEzIb1lR4dgyndojAdHhNpbE4ykOoc75HYEUpzqRYq9Q40PVZmXXbsLSy6pnwdm
   43.57 +8klOj+a1RLgHFWzntaKdBxgEWzTv8wEQAKTfcu95WOyt2MW4myQ0sc2sfoBDXmZb
   43.58 +pE3wm+RnUKxfIwRQvshsp4p96wZZYgjCAxu5eSqxVtD5DbPEUeCbe13vz6b4zxU9
   43.59 +qBRka/Oxgcy8wH9lPpI0Rxz0TkO06farQuxeaPdkto1rb6q1afY8fAvPDIm8ofWw
   43.60 +NLCoo9vHNgUFFnK9kKdkKLQ8/MDDI5A7aTCh0Ihaf27RnS2ygILYla6Fzl4N1UI2
   43.61 +zAK52tYI+7Nb5obSwh4iDHOb62OZmOd6IXMMj69qUwNe73nxahxZnEYb9nMqEhiG
   43.62 +5KpHojvyo5SXUjcvBNAxlENRBbtGh8RFvh6uKmSlb/nJ0+Gca0oVnxNz0xkxtoSS
   43.63 +b2/g9uUypSUnVs2X3HxjLG+yNmh4P2NbtxxZtZ8GTTykwx0eAfv9OMYut4nYwU6Y
   43.64 +a2qB2BEFOEDZHSYD5K3kh1W/jiy8q+RjJJuaTfedyilqBy6bXG1SlHhSQlE03qG2
   43.65 +EADrYMQmMwKfNVC27c9Z4PhmcVLh15VaXiBfx3F76gG23Mdyr6wZntBJnFe9/kCZ
   43.66 +wswiVbMC3hgLg4BdeSWdhzw6n2PNF8Y1kSTMSl+XZdct4y/KY9a9BmCl6aqE3GAJ
   43.67 +VT86GFRbvekNk7PxpmxmCYxu6yI78v0JakM7N4GFIGtznwg5lQO+zI0+vObJzkJz
   43.68 +3QuXkAgNk/D5ABEBAAEAD/9RzMlGFjMv7kr8Qky6yGglDr2Onp4h87rPrp5x0m6c
   43.69 +rvD9LaCSaRIw7KtkMYmppbSMF1gN/gGAWejZ9X/YC9xH7Xjds1lvBPG0+GP0+u9W
   43.70 +6JkrO8yxFj3oGDASeJqR90OTaX3zrGTEQncbP7nMYRL8euQSHGRQtPVXZrwxiSN8
   43.71 +lxsdn367Bj8Jua5/ZvHomAlnBwmi9R9/mIE+0G7hPFr7RUjvsjZKDUzn11VDVx6m
   43.72 +TOTRpJSKBcOewk+l0WmjAx3Aao2JbxuzsvDm1Aec/DrEMBB07/0dbSPbdgIAczCE
   43.73 +wxUX6vyc3NReF79bvLEUiLO8nhXLbKdjS6cTqxsyHQsYfES4nOaBonxWJxRx/ARU
   43.74 +yA1jsgNG04eSSCAuv25bnHGM2PJaeeYAmiaVlIqqbnJbrpWf7yWImLM7F8HNpG92
   43.75 +xBuO1TKvDG764qQ3bmiQTRm4EZYhmy6Q6Q2uPddJy0c5k5RRT9JSlFKNb6HYYYYC
   43.76 +h7YmXInsFSfV9LW0DNVd3MWTfOQxEs4U4XM020KMo9EfOTRa4DAIC0Rt0n4fXF4e
   43.77 +FYwtxubhQmXE2baeqm6IWtNHO/z1M96kwIsoSwRERckTCdl+Ub4rQ3NeExzaQhyK
   43.78 +R2F+GNuUCJK3pGhJV72mTqPHc1dNGqLauIlLalL1o+sLgIB3qPblsyjupZCipZsP
   43.79 +PwgAzReGbpzCoE2+WVFhwz1g9hqg4rrGICfggP0dDQrfJcui7rKaabvAjARzqmWs
   43.80 +9nVklfSBtptRhc0y1gcriOydWAH2pNJdwA78S/5cIeQoe+vYGAs1g0/MjALDpm/H
   43.81 +wQwm9KyJ4pqIG482W8LDTT3OPsscjH3ykZXs0d006ZxdLnsHWIw/TIrLYMrBYwua
   43.82 +CPC9Gyu5UruahOXD+BjBa44+wpz24YhkKON0eL3l+yxOE780pelR0pJhttEALJKm
   43.83 +SXAhxkf/NfgCj9QB5QME6PfWc4d2ImcLzmpVlD69jJddpKaWE9rzArsgxV5BB8S+
   43.84 +FjGfFSl1HVHweQZEYjp8zfSg8wgAzcw413m2KOXJE0K6b/tFkrEU1DkZuJq4yeKD
   43.85 +e5KXtMZksB/z1/y9rM1a+Y0I801poLgdj+CCh2n2AEvBCu3K+dOtbjsvRmY5DmoV
   43.86 +BoA9c3JWvxlfE4FDS1TgFO24apRwkYd1SirbALksQ/w+uknsY/fachKblIQR2Nl5
   43.87 +REFa4hWiMuL0LxJPQy2Uz01XLUf4r4UXpVBap3lMPrFnnypiejRFLQfHjRv+ZrWz
   43.88 +rhBXS1OMta9c3TLYYtVyH6o2/WFe7AEDDIBsT4gt+NJ005Rw6kDqw7pNTEILoioQ
   43.89 +FmvygCB7C0Wkary0ZwY8z5fEQ5lPH8EheWp2bJ31gq2Nb3RBYwgAl1oFTlqsVk0r
   43.90 +TT0daih1vtDKnPUCh2T7jJiGOJm+3cSFNShXgTWV5c29j/pj0HMTNvAtcyldock9
   43.91 +/F1oHUWe0Oe6PiuqZ77vWA8cnu4Bd5gpuOdGD23SNMR485/cB3Dfz2LlNEF939u0
   43.92 +gMrDdWwHCG/z34sqQeOTueudHyHwhKYvwRdOakukqCjjABdKiWsU1FDMugk+X8HE
   43.93 +rXJK/609ocYglF6i7l+UNstsUePhNQFuzt52Zyx3qXiUtaaHbE1DRbeKttxcUd+V
   43.94 +brdqXrW7+RUiF30Lng6p+FTUUeg7Dn4XsrAB5cCaJW7K18BJFjBZ5o1BWe89WDA2
   43.95 +oF5gP5yk8XrgiQI8BBgBCgAmFiEEZq9YBKh5HgG0BxJayvDYOBVCScQFAls07/MC
   43.96 +GwwFCQeGH4AACgkQyvDYOBVCScRr2A//d/yefIKjlnDL00TIRfb/6ONlbkzo/NBN
   43.97 +BRr4mMQ06JNA26HNVAQmoQi2km1VDT7Cou0VmqIMVipoGpkQb1I7hBqT2o0l+caj
   43.98 +aP7bBmqG8nIQ90m4SEDUSoXHYuhf8yRrn2ld3mxgDoAQEnJ4QDuf85J+slUBeLBz
   43.99 +jodXu1c0nusS8KNCPc9JpOKpCx4PC6LpqAhS3+wPsPAeFR6k3H1BIBcwOg0d+jEr
  43.100 +iYFsd3CpKWgIS5nc61EP/QFu/HnMpGndHjwIwyDno7acA4EOb4VmDllXxs+Kgq/q
  43.101 +zqNrtS9HioQraY0LzNFNdclkNYs5ekCqO8lHfYsrYaLaVFGjV8qKwvrk5+HaMxOw
  43.102 +rZkMs0NhTPv3aATr8un1p1jvEtU8f7lGLfeF0ZKIPCpJ81gZXmz+23TmGj/FDOvj
  43.103 +s0go70/lvfz5HKyNnoWMfsOhKQYREmD1NV9TuXyARFan28nD1EYpk202oPbiGBWA
  43.104 +p58Ei4VdBZg5Mcp/Dspos520EKsfETmL5wjQ59enzJWpS8nvxnFhjXx5RdJB/ceK
  43.105 +GmVglYTHpl1btG7KsmQG2Sio+bDveqPsWoQzNrsfqsOb0C+Al+p7lekKtmq5pgTK
  43.106 +5hHu1MkJpQwoNPq6dKizkOVSN4PuDu6qcal8DOK36WeLn/zUgHE4qiqmT/aQNfAs
  43.107 +a2xcFk9WQls=
  43.108 +=ObG3
  43.109 +-----END PGP PRIVATE KEY BLOCK-----
    44.1 --- a/pEpForiOSTests/ServiceChainExecutorTests.swift	Wed Jul 04 11:24:51 2018 +0200
    44.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    44.3 @@ -1,106 +0,0 @@
    44.4 - //
    44.5 -//  ServiceChainExecutorTests.swift
    44.6 -//  pEpForiOS
    44.7 -//
    44.8 -//  Created by Dirk Zimmermann on 07.07.17.
    44.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   44.10 -//
   44.11 -
   44.12 -import XCTest
   44.13 -
   44.14 -@testable import MessageModel
   44.15 -@testable import pEpForiOS
   44.16 -
   44.17 -class ServiceChainExecutorTests: XCTestCase {
   44.18 -    var persistentSetup: PersistentSetup!
   44.19 -
   44.20 -    var cdAccount: CdAccount!
   44.21 -    var cdAccountDisfunctional: CdAccount!
   44.22 -
   44.23 -    override func setUp() {
   44.24 -        super.setUp()
   44.25 -        persistentSetup = PersistentSetup()
   44.26 -
   44.27 -        let cdAccount = SecretTestData().createWorkingCdAccount()
   44.28 -        TestUtil.skipValidation()
   44.29 -        Record.saveAndWait()
   44.30 -        self.cdAccount = cdAccount
   44.31 -    }
   44.32 -
   44.33 -    func runServices(useDisfunctionalAccount: Bool, expectError: Bool) {
   44.34 -        guard let theCdAccount = cdAccount else {
   44.35 -            XCTFail()
   44.36 -            return
   44.37 -        }
   44.38 -
   44.39 -        let outgoingMailsToSend = try! TestUtil.createOutgoingMails(
   44.40 -            cdAccount: theCdAccount, testCase: self, numberOfMails: 3)
   44.41 -        XCTAssertGreaterThan(outgoingMailsToSend.count, 0)
   44.42 -
   44.43 -        if useDisfunctionalAccount {
   44.44 -            TestUtil.makeServersUnreachable(cdAccount: theCdAccount)
   44.45 -        }
   44.46 -
   44.47 -        guard let (imapSyncData, smtpSendData) = TestUtil.syncData(cdAccount: theCdAccount) else {
   44.48 -            XCTFail()
   44.49 -            return
   44.50 -        }
   44.51 -
   44.52 -        let expBackgroundAllTasksBackgrounded: XCTestExpectation? = expectError ? nil : expectation(
   44.53 -            description: "expBackgroundAllTasksBackgrounded")
   44.54 -        expBackgroundAllTasksBackgrounded?.assertForOverFulfill = true
   44.55 -        expBackgroundAllTasksBackgrounded?.expectedFulfillmentCount = 4
   44.56 -        let backgrounder = MockBackgrounder(
   44.57 -            expBackgroundTaskFinishedAtLeastOnce: expBackgroundAllTasksBackgrounded)
   44.58 -
   44.59 -        let smtpSentDelegate = TestSmtpSendServiceDelegate()
   44.60 -        let smtpService = SmtpSendService(
   44.61 -            parentName: #function, backgrounder: backgrounder,
   44.62 -            imapSyncData: imapSyncData, smtpSendData: smtpSendData)
   44.63 -        smtpService.delegate = smtpSentDelegate
   44.64 -
   44.65 -        let syncFoldersService = SyncFoldersFromServerService(
   44.66 -            parentName: #function, backgrounder: backgrounder, imapSyncData: imapSyncData)
   44.67 -
   44.68 -        let fetchMessagesService = FetchMessagesService(
   44.69 -            parentName: #function, backgrounder: backgrounder, imapSyncData: imapSyncData)
   44.70 -
   44.71 -        let syncMessagesService = SyncExistingMessagesService(
   44.72 -            parentName: #function, backgrounder: backgrounder, imapSyncData: imapSyncData)
   44.73 -
   44.74 -        let chainedService = ServiceChainExecutor()
   44.75 -        chainedService.add(services: [smtpService, syncFoldersService,
   44.76 -                                      fetchMessagesService, syncMessagesService])
   44.77 -
   44.78 -        let expectationAllServicesExecuted = expectation(
   44.79 -            description: "expectationAllServicesExecuted")
   44.80 -        chainedService.execute() { error in
   44.81 -            if error == nil {
   44.82 -                XCTAssertEqual(smtpSentDelegate.successfullySentMessageIDs.count,
   44.83 -                               outgoingMailsToSend.count)
   44.84 -            } else {
   44.85 -                XCTAssertLessThan(smtpSentDelegate.successfullySentMessageIDs.count,
   44.86 -                                  outgoingMailsToSend.count)
   44.87 -            }
   44.88 -
   44.89 -            if expectError {
   44.90 -                XCTAssertNotNil(error)
   44.91 -            } else {
   44.92 -                XCTAssertNil(error)
   44.93 -            }
   44.94 -
   44.95 -            expectationAllServicesExecuted.fulfill()
   44.96 -        }
   44.97 -        waitForExpectations(timeout: TestUtil.waitTime) { error in
   44.98 -            XCTAssertNil(error)
   44.99 -        }
  44.100 -    }
  44.101 -
  44.102 -    func testBasicOK() {
  44.103 -        runServices(useDisfunctionalAccount: false, expectError: false)
  44.104 -    }
  44.105 -
  44.106 -    func testBasicError() {
  44.107 -        runServices(useDisfunctionalAccount: true, expectError: true)
  44.108 -    }
  44.109 -}
    45.1 --- a/pEpForiOSTests/ServiceFactoryTests.swift	Wed Jul 04 11:24:51 2018 +0200
    45.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    45.3 @@ -1,102 +0,0 @@
    45.4 -//
    45.5 -//  ServiceFactoryTests.swift
    45.6 -//  pEpForiOS
    45.7 -//
    45.8 -//  Created by Dirk Zimmermann on 10.07.17.
    45.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   45.10 -//
   45.11 -
   45.12 -import XCTest
   45.13 -
   45.14 -@testable import MessageModel
   45.15 -@testable import pEpForiOS
   45.16 -
   45.17 -class ServiceFactoryTests: XCTestCase {
   45.18 -    var persistentSetup: PersistentSetup!
   45.19 -
   45.20 -    var cdAccount: CdAccount!
   45.21 -    var cdAccountDisfunctional: CdAccount!
   45.22 -
   45.23 -    override func setUp() {
   45.24 -        super.setUp()
   45.25 -        persistentSetup = PersistentSetup()
   45.26 -
   45.27 -        let cdAccount = SecretTestData().createWorkingCdAccount()
   45.28 -
   45.29 -        TestUtil.skipValidation()
   45.30 -        Record.saveAndWait()
   45.31 -        self.cdAccount = cdAccount
   45.32 -    }
   45.33 -
   45.34 -    func runServices(functionName: String, useDisfunctionalAccount: Bool, expectError: Bool) {
   45.35 -        guard let theCdAccount = cdAccount else {
   45.36 -            XCTFail()
   45.37 -            return
   45.38 -        }
   45.39 -
   45.40 -        let outgoingMailsToSend = try! TestUtil.createOutgoingMails(
   45.41 -            cdAccount: theCdAccount, testCase: self, numberOfMails: 3)
   45.42 -        XCTAssertGreaterThan(outgoingMailsToSend.count, 0)
   45.43 -
   45.44 -        let cdMessagesBefore = EncryptAndSendOperation.outgoingMails(
   45.45 -            context: Record.Context.default, cdAccount: cdAccount)
   45.46 -        XCTAssertEqual(cdMessagesBefore.count, outgoingMailsToSend.count)
   45.47 -
   45.48 -        if useDisfunctionalAccount {
   45.49 -            TestUtil.makeServersUnreachable(cdAccount: theCdAccount)
   45.50 -        }
   45.51 -
   45.52 -        guard let (imapSyncData, smtpSendData) = TestUtil.syncData(cdAccount: theCdAccount) else {
   45.53 -            XCTFail()
   45.54 -            return
   45.55 -        }
   45.56 -
   45.57 -        let expBackgroundAllTasksBackgrounded: XCTestExpectation? =
   45.58 -            expectError ? nil : expectation(description: "expBackgroundAllTasksBackgrounded")
   45.59 -        expBackgroundAllTasksBackgrounded?.assertForOverFulfill = true
   45.60 -        expBackgroundAllTasksBackgrounded?.expectedFulfillmentCount = 5
   45.61 -        let backgrounder = MockBackgrounder(
   45.62 -            expBackgroundTaskFinishedAtLeastOnce: expBackgroundAllTasksBackgrounded)
   45.63 -
   45.64 -        let serviceFactory = ServiceFactory()
   45.65 -        let smtpSentDelegate = TestSmtpSendServiceDelegate()
   45.66 -        let service = serviceFactory.initialSync(
   45.67 -            parentName: functionName, backgrounder: backgrounder,
   45.68 -            imapSyncData: imapSyncData, smtpSendData: smtpSendData,
   45.69 -            smtpSendServiceDelegate: smtpSentDelegate, syncFlagsToServerServiceDelegate: nil)
   45.70 -
   45.71 -        let expectationAllServicesExecuted = expectation(
   45.72 -            description: "expectationAllServicesExecuted")
   45.73 -        service.execute() { error in
   45.74 -            let context = Record.Context.default
   45.75 -            context.performAndWait {
   45.76 -                let cdMessagesAfter = EncryptAndSendOperation.outgoingMails(
   45.77 -                    context: Record.Context.default, cdAccount: self.cdAccount)
   45.78 -
   45.79 -                if error == nil {
   45.80 -                    XCTAssertEqual(cdMessagesAfter.count, 0)
   45.81 -                } else {
   45.82 -                    XCTAssertEqual(cdMessagesAfter.count, outgoingMailsToSend.count)
   45.83 -                }
   45.84 -            }
   45.85 -            if expectError {
   45.86 -                XCTAssertNotNil(error)
   45.87 -            } else {
   45.88 -                XCTAssertNil(error)
   45.89 -            }
   45.90 -
   45.91 -            expectationAllServicesExecuted.fulfill()
   45.92 -        }
   45.93 -        waitForExpectations(timeout: TestUtil.waitTime) { error in
   45.94 -            XCTAssertNil(error)
   45.95 -        }
   45.96 -    }
   45.97 -
   45.98 -    func testInitialSyncOK() {
   45.99 -        runServices(functionName: #function, useDisfunctionalAccount: false, expectError: false)
  45.100 -    }
  45.101 -    
  45.102 -    func testInitialSyncError() {
  45.103 -        runServices(functionName: #function, useDisfunctionalAccount: true, expectError: true)
  45.104 -    }
  45.105 -}
    46.1 --- a/pEpForiOSTests/SimpleOperationsTest.swift	Wed Jul 04 11:24:51 2018 +0200
    46.2 +++ b/pEpForiOSTests/SimpleOperationsTest.swift	Wed Jul 04 11:27:58 2018 +0200
    46.3 @@ -405,15 +405,13 @@
    46.4      // MARK: - EncryptAndSendOperation
    46.5  
    46.6      func testEncryptAndSendOperation() {
    46.7 -        let _ = try! TestUtil.createOutgoingMails(cdAccount: cdAccount,
    46.8 +        // Create mails to send ...
    46.9 +        let sentUUIDs = try! TestUtil.createOutgoingMails(cdAccount: cdAccount,
   46.10                                                    testCase: self,
   46.11 -                                                  numberOfMails: 3)
   46.12 -
   46.13 -        let expMailsSent = expectation(description: "expMailsSent")
   46.14 -
   46.15 +                                                  numberOfMails: 3).map { $0.uuid! }
   46.16 +        // ... Login ...
   46.17          let smtpSendData = SmtpSendData(connectInfo: smtpConnectInfo)
   46.18          let errorContainer = ErrorContainer()
   46.19 -
   46.20          let smtpLogin = LoginSmtpOperation(
   46.21              parentName: #function,
   46.22              smtpSendData: smtpSendData, errorContainer: errorContainer)
   46.23 @@ -421,7 +419,8 @@
   46.24              smtpLogin.completionBlock = nil
   46.25              XCTAssertNotNil(smtpSendData.smtp)
   46.26          }
   46.27 -
   46.28 +        // ... and send them.
   46.29 +        let expMailsSent = expectation(description: "expMailsSent")
   46.30          let sendOp = EncryptAndSendOperation(
   46.31              parentName: #function,
   46.32              smtpSendData: smtpSendData, errorContainer: errorContainer)
   46.33 @@ -432,24 +431,23 @@
   46.34              sendOp.completionBlock = nil
   46.35              expMailsSent.fulfill()
   46.36          }
   46.37 -
   46.38          let queue = OperationQueue()
   46.39          queue.addOperation(smtpLogin)
   46.40          queue.addOperation(sendOp)
   46.41 -
   46.42          waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   46.43              XCTAssertNil(error)
   46.44              XCTAssertFalse(sendOp.hasErrors())
   46.45          })
   46.46 -
   46.47 -        if let msgs = CdMessage.all() as? [CdMessage] {
   46.48 -            for m in msgs {
   46.49 -                XCTAssertEqual(m.sendStatus, SendStatus.smtpDone)
   46.50 +        // Check sent status of all sent mails
   46.51 +        for sentUuid in sentUUIDs {
   46.52 +            let msgs = CdMessage.search(byUUID: sentUuid)
   46.53 +            XCTAssertEqual(msgs.count, 1)
   46.54 +            guard let msg = msgs.first else {
   46.55 +                XCTFail("Missing sent message")
   46.56 +                return
   46.57              }
   46.58 -        } else {
   46.59 -            XCTFail()
   46.60 +             XCTAssertEqual(msg.sendStatus, SendStatus.smtpDone)
   46.61          }
   46.62 -
   46.63          smtpSendData.smtp?.close()
   46.64      }
   46.65  
   46.66 @@ -493,8 +491,10 @@
   46.67          to.userName = "Unit 001"
   46.68          to.address = "unittest.ios.1@peptest.ch"
   46.69  
   46.70 -        let folder = CdFolder.by(folderType: .sent, account: cdAccount)
   46.71 -        XCTAssertNotNil(folder)
   46.72 +        guard let folder = CdFolder.by(folderType: .sent, account: cdAccount) else {
   46.73 +            XCTFail("No folder")
   46.74 +            return
   46.75 +        }
   46.76  
   46.77          // Build emails
   46.78          let numMails = 5
   46.79 @@ -522,10 +522,10 @@
   46.80          }
   46.81  
   46.82          let expSentAppended = expectation(description: "expSentAppended")
   46.83 -
   46.84 -        let appendOp = AppendSendMailsOperation(
   46.85 -            parentName: #function,
   46.86 -            imapSyncData: imapSyncData, errorContainer: errorContainer)
   46.87 +        let appendOp = AppendMailsOperation(parentName: #function,
   46.88 +                                            folder: folder.folder(),
   46.89 +                                            imapSyncData: imapSyncData,
   46.90 +                                            errorContainer: errorContainer)
   46.91          appendOp.completionBlock = {
   46.92              appendOp.completionBlock = nil
   46.93              expSentAppended.fulfill()
   46.94 @@ -580,8 +580,10 @@
   46.95          to.userName = "Unit 001"
   46.96          to.address = "unittest.ios.1@peptest.ch"
   46.97  
   46.98 -        let folder = CdFolder.by(folderType: .drafts, account: cdAccount)
   46.99 -        XCTAssertNotNil(folder)
  46.100 +        guard let folder = CdFolder.by(folderType: .drafts, account: cdAccount) else {
  46.101 +            XCTFail("No folder")
  46.102 +            return
  46.103 +        }
  46.104  
  46.105          // Build emails
  46.106          let numMails = 5
  46.107 @@ -609,9 +611,10 @@
  46.108  
  46.109          let expDraftsStored = expectation(description: "expDraftsStored")
  46.110  
  46.111 -        let appendOp = AppendDraftMailsOperation(
  46.112 -            parentName: #function,
  46.113 -            imapSyncData: imapSyncData, errorContainer: errorContainer)
  46.114 +        let appendOp = AppendMailsOperation(parentName: #function,
  46.115 +                                            folder: folder.folder(),
  46.116 +                                            imapSyncData: imapSyncData,
  46.117 +                                            errorContainer: errorContainer)
  46.118          appendOp.completionBlock = {
  46.119              appendOp.completionBlock = nil
  46.120              expDraftsStored.fulfill()
    47.1 --- a/pEpForiOSTests/SmtpSendServiceTests.swift	Wed Jul 04 11:24:51 2018 +0200
    47.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    47.3 @@ -1,110 +0,0 @@
    47.4 -//
    47.5 -//  SmtpSendServiceTests.swift
    47.6 -//  pEpForiOS
    47.7 -//
    47.8 -//  Created by Dirk Zimmermann on 29.06.17.
    47.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   47.10 -//
   47.11 -
   47.12 -import XCTest
   47.13 -
   47.14 -@testable import MessageModel
   47.15 -@testable import pEpForiOS
   47.16 -
   47.17 -class TestSmtpSendServiceDelegate: SmtpSendServiceDelegate {
   47.18 -    var successfullySentMessageIDs = [MessageID]()
   47.19 -
   47.20 -    func sent(messageIDs: [MessageID]) {
   47.21 -        successfullySentMessageIDs = messageIDs
   47.22 -    }
   47.23 -}
   47.24 -
   47.25 -class SmtpSendServiceTests: XCTestCase {
   47.26 -    var persistentSetup: PersistentSetup!
   47.27 -
   47.28 -    var cdAccount: CdAccount!
   47.29 -    var cdAccountDisfunctional: CdAccount!
   47.30 -
   47.31 -    override func setUp() {
   47.32 -        super.setUp()
   47.33 -        persistentSetup = PersistentSetup()
   47.34 -
   47.35 -        let cdAccount = SecretTestData().createWorkingCdAccount()
   47.36 -        TestUtil.skipValidation()
   47.37 -        Record.saveAndWait()
   47.38 -        self.cdAccount = cdAccount
   47.39 -
   47.40 -        let cdDisfunctionalAccount = SecretTestData().createDisfunctionalCdAccount()
   47.41 -        TestUtil.skipValidation()
   47.42 -        Record.saveAndWait()
   47.43 -        self.cdAccountDisfunctional = cdDisfunctionalAccount
   47.44 -    }
   47.45 -
   47.46 -    func syncData(cdAccount: CdAccount) -> (ImapSyncData, SmtpSendData)? {
   47.47 -        guard
   47.48 -            let imapCI = cdAccount.imapConnectInfo,
   47.49 -            let smtpCI = cdAccount.smtpConnectInfo else {
   47.50 -                XCTFail()
   47.51 -                return nil
   47.52 -        }
   47.53 -        return (ImapSyncData(connectInfo: imapCI), SmtpSendData(connectInfo: smtpCI))
   47.54 -    }
   47.55 -
   47.56 -    func runSmtpSendService(shouldSucceed: Bool, verifyError: @escaping ServiceFinishedHandler) {
   47.57 -        guard let theCdAccount = cdAccount else {
   47.58 -            XCTFail()
   47.59 -            return
   47.60 -        }
   47.61 -
   47.62 -        let outgoingMailsToSend = try! TestUtil.createOutgoingMails(
   47.63 -            cdAccount: theCdAccount, testCase: self, numberOfMails: 3)
   47.64 -        XCTAssertGreaterThan(outgoingMailsToSend.count, 0)
   47.65 -
   47.66 -        if !shouldSucceed {
   47.67 -            TestUtil.makeServersUnreachable(cdAccount: theCdAccount)
   47.68 -        }
   47.69 -
   47.70 -        guard let (imapSyncData, smtpSendData) = TestUtil.syncData(cdAccount: theCdAccount) else {
   47.71 -            XCTFail()
   47.72 -            return
   47.73 -        }
   47.74 -
   47.75 -        let expBackgroundTaskFinishedAtLeastOnce = expectation(
   47.76 -            description: "expectationBackgrounded")
   47.77 -        let backgrounder = MockBackgrounder(
   47.78 -            expBackgroundTaskFinishedAtLeastOnce: expBackgroundTaskFinishedAtLeastOnce)
   47.79 -
   47.80 -        let smtpSentDelegate = TestSmtpSendServiceDelegate()
   47.81 -        let smtpService = SmtpSendService(
   47.82 -            parentName: #function, backgrounder: backgrounder,
   47.83 -            imapSyncData: imapSyncData, smtpSendData: smtpSendData)
   47.84 -        smtpService.delegate = smtpSentDelegate
   47.85 -        let expectationSmtpExecuted = expectation(description: "expectationSmtpExecuted")
   47.86 -        smtpService.execute() { error in
   47.87 -            if error == nil {
   47.88 -                XCTAssertEqual(smtpSentDelegate.successfullySentMessageIDs.count,
   47.89 -                               outgoingMailsToSend.count)
   47.90 -            } else {
   47.91 -                XCTAssertLessThan(smtpSentDelegate.successfullySentMessageIDs.count,
   47.92 -                                  outgoingMailsToSend.count)
   47.93 -            }
   47.94 -            verifyError(error)
   47.95 -            expectationSmtpExecuted.fulfill()
   47.96 -        }
   47.97 -        waitForExpectations(timeout: TestUtil.waitTime) { error in
   47.98 -            XCTAssertNil(error)
   47.99 -        }
  47.100 -    }
  47.101 -
  47.102 -    func testSmtpSendServiceOk() {
  47.103 -        runSmtpSendService(shouldSucceed: true) { error in
  47.104 -            XCTAssertNil(error)
  47.105 -        }
  47.106 -    }
  47.107 -
  47.108 -    func testSmtpSendServiceError() {
  47.109 -        runSmtpSendService(shouldSucceed: false) { error in
  47.110 -            XCTAssertNotNil(error)
  47.111 -        }
  47.112 -    }
  47.113 -}
    48.1 --- a/pEpForiOSTests/SyncExistingMessagesServiceTests.swift	Wed Jul 04 11:24:51 2018 +0200
    48.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    48.3 @@ -1,79 +0,0 @@
    48.4 -//
    48.5 -//  SyncExistingMessagesServiceTests.swift
    48.6 -//  pEpForiOS
    48.7 -//
    48.8 -//  Created by Dirk Zimmermann on 06.07.17.
    48.9 -//  Copyright © 2017 p≡p Security S.A. All rights reserved.
   48.10 -//
   48.11 -
   48.12 -import XCTest
   48.13 -
   48.14 -@testable import MessageModel
   48.15 -@testable import pEpForiOS
   48.16 -
   48.17 -class SyncExistingMessagesServiceTests: XCTestCase {
   48.18 -    var persistentSetup: PersistentSetup!
   48.19 -
   48.20 -    var cdAccount: CdAccount!
   48.21 -
   48.22 -    override func setUp() {
   48.23 -        super.setUp()
   48.24 -        persistentSetup = PersistentSetup()
   48.25 -
   48.26 -        let cdAccount = SecretTestData().createWorkingCdAccount()
   48.27 -        TestUtil.skipValidation()
   48.28 -        Record.saveAndWait()
   48.29 -        self.cdAccount = cdAccount
   48.30 -    }
   48.31 -
   48.32 -    func runSyncTest(parentName: String, folderName: String = ImapSync.defaultImapInboxName,
   48.33 -                     useDisfunctionalAccount: Bool, expectError: Bool) {
   48.34 -        // the fetch should actually always work
   48.35 -        TestUtil.runFetchTest(parentName: parentName, testCase: self, cdAccount: cdAccount,
   48.36 -                              useDisfunctionalAccount: false,
   48.37 -                              folderName:  ImapSync.defaultImapInboxName,
   48.38 -                              expectError: false)
   48.39 -
   48.40 -        if useDisfunctionalAccount {
   48.41 -            TestUtil.makeServersUnreachable(cdAccount: cdAccount)
   48.42 -        }
   48.43 -
   48.44 -        guard let (imapSyncData, _) = TestUtil.syncData(cdAccount: cdAccount) else {
   48.45 -            XCTFail()
   48.46 -            return
   48.47 -        }
   48.48 -
   48.49 -        let expectationBackgrounded = expectation(description: "expectationBackgrounded")
   48.50 -        let mbg = MockBackgrounder(expBackgroundTaskFinishedAtLeastOnce: expectationBackgrounded)
   48.51 -        let service = SyncExistingMessagesService(
   48.52 -            parentName: #function, backgrounder: mbg, imapSyncData: imapSyncData,
   48.53 -            folderName: folderName)
   48.54 -
   48.55 -        service.execute() { error in
   48.56 -            if expectError {
   48.57 -                XCTAssertNotNil(error)
   48.58 -            } else {
   48.59 -                XCTAssertNil(error)
   48.60 -            }
   48.61 -        }
   48.62 -
   48.63 -        waitForExpectations(timeout: TestUtil.waitTime) { error in
   48.64 -            XCTAssertNil(error)
   48.65 -        }
   48.66 -    }
   48.67 -
   48.68 -    func testAfterFetchingOK() {
   48.69 -        runSyncTest(parentName: #function, folderName: "INbox", useDisfunctionalAccount: false,
   48.70 -                    expectError: false)
   48.71 -    }
   48.72 -
   48.73 -    func testAfterFetchingError() {
   48.74 -        runSyncTest(parentName: #function, folderName: "inbox", useDisfunctionalAccount: true,
   48.75 -                    expectError: true)
   48.76 -    }
   48.77 -
   48.78 -    func testWrongFolder() {
   48.79 -        runSyncTest(parentName: #function, folderName: "wrongDoesNotExist",
   48.80 -                    useDisfunctionalAccount: false, expectError: true)
   48.81 -    }
   48.82 -}
    49.1 --- a/pEpForiOSTests/TestUtils/CdAccount+TestUtils.swift	Wed Jul 04 11:24:51 2018 +0200
    49.2 +++ b/pEpForiOSTests/TestUtils/CdAccount+TestUtils.swift	Wed Jul 04 11:27:58 2018 +0200
    49.3 @@ -39,9 +39,7 @@
    49.4          let imapSyncData = ImapSyncData(connectInfo: imapConnectInfo)
    49.5          XCTAssertNotNil(imapConnectInfo)
    49.6  
    49.7 -
    49.8 -        let imapLogin = LoginImapOperation(
    49.9 -            parentName: #function, imapSyncData: imapSyncData)
   49.10 +        let imapLogin = LoginImapOperation(parentName: #function, imapSyncData: imapSyncData)
   49.11  
   49.12          let expFoldersFetched = testCase.expectation(description: "expFoldersFetched")
   49.13          let syncFoldersOp = SyncFoldersFromServerOperation(
    50.1 --- a/pEpForiOSTests/TestUtils/TestUtil.swift	Wed Jul 04 11:24:51 2018 +0200
    50.2 +++ b/pEpForiOSTests/TestUtils/TestUtil.swift	Wed Jul 04 11:27:58 2018 +0200
    50.3 @@ -264,59 +264,6 @@
    50.4          }
    50.5      }
    50.6  
    50.7 -    class FetchMessagesServiceTestDelegate: FetchMessagesServiceDelegate {
    50.8 -        var fetchedMessages = [Message]()
    50.9 -
   50.10 -        func didFetch(message: Message) {
   50.11 -            fetchedMessages.append(message)
   50.12 -        }
   50.13 -    }
   50.14 -
   50.15 -    static func runFetchTest(parentName: String, testCase: XCTestCase, cdAccount: CdAccount,
   50.16 -                             useDisfunctionalAccount: Bool,
   50.17 -                             folderName: String = ImapSync.defaultImapInboxName,
   50.18 -                             expectError: Bool) {
   50.19 -        if useDisfunctionalAccount {
   50.20 -            TestUtil.makeServersUnreachable(cdAccount: cdAccount)
   50.21 -        }
   50.22 -
   50.23 -        guard let (imapSyncData, _) = TestUtil.syncData(cdAccount: cdAccount) else {
   50.24 -            XCTFail()
   50.25 -            return
   50.26 -        }
   50.27 -
   50.28 -        let expectationServiceRan = testCase.expectation(description: "expectationServiceRan")
   50.29 -        let mbg = MockBackgrounder(expBackgroundTaskFinishedAtLeastOnce: expectationServiceRan)
   50.30 -
   50.31 -        let service = FetchMessagesService(parentName: parentName, backgrounder: mbg,
   50.32 -                                           imapSyncData: imapSyncData, folderName: folderName)
   50.33 -        let testDelegate = FetchMessagesServiceTestDelegate()
   50.34 -        service.delegate = testDelegate
   50.35 -
   50.36 -        let expServiceBlockInvoked = testCase.expectation(description: "expServiceBlockInvoked")
   50.37 -        service.execute() { error in
   50.38 -            expServiceBlockInvoked.fulfill()
   50.39 -
   50.40 -            if expectError {
   50.41 -                XCTAssertNotNil(error)
   50.42 -            } else {
   50.43 -                XCTAssertNil(error)
   50.44 -            }
   50.45 -        }
   50.46 -
   50.47 -        testCase.waitForExpectations(timeout: TestUtil.waitTime) { error in
   50.48 -            XCTAssertNil(error)
   50.49 -        }
   50.50 -
   50.51 -        if expectError {
   50.52 -            XCTAssertEqual(testDelegate.fetchedMessages.count, 0)
   50.53 -        } else {
   50.54 -            XCTAssertGreaterThan(testDelegate.fetchedMessages.count, 0)
   50.55 -        }
   50.56 -
   50.57 -        imapSyncData.sync?.close()
   50.58 -    }
   50.59 -
   50.60      // MARK: - Sync Loop
   50.61  
   50.62      static public func syncAndWait(numAccountsToSync: Int = 1, testCase: XCTestCase, skipValidation: Bool) {
   50.63 @@ -343,11 +290,11 @@
   50.64              XCTAssertNil(error)
   50.65          }
   50.66  
   50.67 -        TestUtil.cancelNetworkService(networkService: networkService, testCase: testCase)
   50.68 +        TestUtil.cancelNetworkServiceAndWait(networkService: networkService, testCase: testCase)
   50.69      }
   50.70  
   50.71      // MARK: - NetworkService
   50.72 -    static public func cancelNetworkService(networkService: NetworkService, testCase: XCTestCase) {
   50.73 +    static public func cancelNetworkServiceAndWait(networkService: NetworkService, testCase: XCTestCase) {
   50.74          let del = NetworkServiceObserver(
   50.75              expCanceled: testCase.expectation(description: "expCanceled"))
   50.76          networkService.unitTestDelegate = del
   50.77 @@ -359,14 +306,71 @@
   50.78          })
   50.79      }
   50.80  
   50.81 -    // MARK: Messages
   50.82 +    // MARK: - Messages
   50.83  
   50.84 -    static func createOutgoingMails(cdAccount: CdAccount,
   50.85 +    /// Calls createOutgoingMails for Cd...Objcets. See docs there.
   50.86 +    static func createOutgoingMails(account: Account,
   50.87 +                                    fromIdentity: Identity? = nil,
   50.88 +                                    toIdentity: Identity? = nil,
   50.89 +                                    setSentTimeOffsetForManualOrdering: Bool = false,
   50.90                                      testCase: XCTestCase,
   50.91                                      numberOfMails: Int,
   50.92                                      withAttachments: Bool = true,
   50.93                                      attachmentsInlined: Bool = false,
   50.94 -                                    encrypt: Bool = true) throws -> [CdMessage] {
   50.95 +                                    encrypt: Bool = true,
   50.96 +                                    forceUnencrypted: Bool = false) throws -> [Message] {
   50.97 +        guard
   50.98 +            let cdAccount = account.cdAccount(),
   50.99 +            let cdFromIdentity = fromIdentity?.cdIdentity(),
  50.100 +            let cdToIdentity = toIdentity?.cdIdentity()
  50.101 +            else {
  50.102 +                XCTFail("No account.")
  50.103 +                return []
  50.104 +        }
  50.105 +
  50.106 +        let cdMessages = try createOutgoingMails(cdAccount: cdAccount,
  50.107 +                                                 fromIdentity: cdFromIdentity,
  50.108 +                                                 toIdentity: cdToIdentity,
  50.109 +                                                 setSentTimeOffsetForManualOrdering: setSentTimeOffsetForManualOrdering,
  50.110 +                                                 testCase: testCase,
  50.111 +                                                 numberOfMails: numberOfMails,
  50.112 +                                                 withAttachments: withAttachments,
  50.113 +                                                 attachmentsInlined: attachmentsInlined,
  50.114 +                                                 encrypt: encrypt,
  50.115 +                                                 forceUnencrypted: forceUnencrypted)
  50.116 +        return cdMessages.map { $0.message()! }
  50.117 +    }
  50.118 +
  50.119 +    /// Creates outgoing messages
  50.120 +    ///
  50.121 +    /// - Parameters:
  50.122 +    ///   - cdAccount: account to send from. Is ignored if fromIdentity is not nil 
  50.123 +    ///   - fromIdentity: identity used as sender
  50.124 +    ///   - toIdentity: identity used as recipient
  50.125 +    ///   - setSentTimeOffsetForManualOrdering: Add some time difference to date sent tp be
  50.126 +    ///                                         recognised by Date().sort. That makes it easier to
  50.127 +    ///                                         misuse thoses mails for manual debugging.
  50.128 +    //
  50.129 +    ///   - testCase: the one to make fail
  50.130 +    ///   - numberOfMails: num mails to create
  50.131 +    ///   - withAttachments: Whether or not messages should contain attachments
  50.132 +    ///   - attachmentsInlined: Whether or not the attachments should be inlined
  50.133 +    ///   - encrypt: Whether or not to import a key for the receipient. Is ignored if `toIdentity`
  50.134 +    ///              is not nil
  50.135 +    ///   - forceUnencrypted: mark mails force unencrypted
  50.136 +    /// - Returns: created mails
  50.137 +    /// - Throws: error importing key
  50.138 +    static func createOutgoingMails(cdAccount: CdAccount,
  50.139 +                                    fromIdentity: CdIdentity? = nil,
  50.140 +                                    toIdentity: CdIdentity? = nil,
  50.141 +                                    setSentTimeOffsetForManualOrdering: Bool = false,
  50.142 +                                    testCase: XCTestCase,
  50.143 +                                    numberOfMails: Int,
  50.144 +                                    withAttachments: Bool = true,
  50.145 +                                    attachmentsInlined: Bool = false,
  50.146 +                                    encrypt: Bool = true,
  50.147 +                                    forceUnencrypted: Bool = false) throws -> [CdMessage] {
  50.148 +        let cdAccount = fromIdentity?.accounts?.allObjects.first as? CdAccount ?? cdAccount 
  50.149          testCase.continueAfterFailure = false
  50.150  
  50.151          if numberOfMails == 0 {
  50.152 @@ -376,20 +380,8 @@
  50.153          let existingSentFolder = CdFolder.by(folderType: .sent, account: cdAccount)
  50.154  
  50.155          if existingSentFolder == nil {
  50.156 -            let expectationFoldersFetched = testCase.expectation(
  50.157 -                description: "expectationFoldersFetched")
  50.158 -            guard let imapCI = cdAccount.imapConnectInfo else {
  50.159 -                XCTFail()
  50.160 -                return []
  50.161 -            }
  50.162 -            let imapSyncData = ImapSyncData(connectInfo: imapCI)
  50.163 -            let fs = SyncFoldersFromServerService(parentName: #function, imapSyncData: imapSyncData)
  50.164 -            fs.execute() { error in
  50.165 -                XCTAssertNil(error)
  50.166 -                expectationFoldersFetched.fulfill()
  50.167 -            }
  50.168 -
  50.169 -            testCase.wait(for: [expectationFoldersFetched], timeout: waitTime)
  50.170 +            // Make sure folders are synced
  50.171 +            syncAndWait(testCase: testCase, skipValidation: true)
  50.172          }
  50.173  
  50.174          guard let sentFolder = CdFolder.by(folderType: .sent, account: cdAccount) else {
  50.175 @@ -397,27 +389,33 @@
  50.176              return []
  50.177          }
  50.178  
  50.179 -        if encrypt {
  50.180 -            let session = PEPSession()
  50.181 -            try TestUtil.importKeyByFileName(
  50.182 -                session, fileName: "Unit 1 unittest.ios.1@peptest.ch (0x9CB8DBCC) pub.asc")
  50.183 +        let from: CdIdentity
  50.184 +        if let fromIdentity = fromIdentity {
  50.185 +            from = fromIdentity
  50.186 +        } else {
  50.187 +            from = CdIdentity.create()
  50.188 +            from.userName = cdAccount.identity?.userName ?? "Unit 004"
  50.189 +            from.address = cdAccount.identity?.address ?? "unittest.ios.4@peptest.ch"
  50.190          }
  50.191 -
  50.192 -        let from = CdIdentity.create()
  50.193 -        from.userName = cdAccount.identity?.userName ?? "Unit 004"
  50.194 -        from.address = cdAccount.identity?.address ?? "unittest.ios.4@peptest.ch"
  50.195          guard let fromUserId = cdAccount.identity?.userID else {
  50.196              fatalError("No userId")
  50.197          }
  50.198          from.userID = fromUserId
  50.199  
  50.200 -        let toWithKey = CdIdentity.create()
  50.201 -        toWithKey.userName = "Unit 001"
  50.202 -        toWithKey.address = "unittest.ios.1@peptest.ch"
  50.203 -
  50.204 -        let toWithoutKey = CdIdentity.create()
  50.205 -        toWithoutKey.userName = "Unit 002"
  50.206 -        toWithoutKey.address = "unittest.ios.2@peptest.ch"
  50.207 +        let to: CdIdentity
  50.208 +        if let toIdentity = toIdentity {
  50.209 +            to = toIdentity
  50.210 +        } else {
  50.211 +            if encrypt {
  50.212 +                let session = PEPSession()
  50.213 +                try TestUtil.importKeyByFileName(
  50.214 +                    session, fileName: "Unit 1 unittest.ios.1@peptest.ch (0x9CB8DBCC) pub.asc")
  50.215 +            }
  50.216 +            let toWithKey = CdIdentity.create()
  50.217 +            toWithKey.userName = "Unit 001"
  50.218 +            toWithKey.address = "unittest.ios.1@peptest.ch"
  50.219 +            to = toWithKey
  50.220 +        }
  50.221  
  50.222          let imageFileName = "PorpoiseGalaxy_HubbleFraile_960.jpg"
  50.223          guard let imageData = TestUtil.loadData(fileName: imageFileName) else {
  50.224 @@ -434,11 +432,16 @@
  50.225              message.shortMessage = "Some subject \(i)"
  50.226              message.longMessage = "Long message \(i)"
  50.227              message.longMessageFormatted = "<h1>Long HTML \(i)</h1>"
  50.228 -            // Add some time difference recognised by Date().sort.
  50.229 -            // That makes it easier to misuse thoses mails for manual debugging.
  50.230 -            let sentTimeOffset = Double(i) - 1
  50.231 -            message.sent = Date().addingTimeInterval(sentTimeOffset)
  50.232 -            message.addTo(cdIdentity: toWithKey)
  50.233 +            message.pEpProtected = !forceUnencrypted
  50.234 +            if setSentTimeOffsetForManualOrdering {
  50.235 +                // Add some time difference recognised by Date().sort.
  50.236 +                // That makes it easier to misuse thoses mails for manual debugging.
  50.237 +                let sentTimeOffset = Double(i) - 1
  50.238 +                message.sent = Date().addingTimeInterval(sentTimeOffset)
  50.239 +            } else {
  50.240 +                message.sent = Date()
  50.241 +            }
  50.242 +            message.addTo(cdIdentity: to)
  50.243  
  50.244              // add attachments
  50.245              if withAttachments {
  50.246 @@ -458,8 +461,9 @@
  50.247  
  50.248          if let cdOutgoingMsgs = sentFolder.messages?.sortedArray(
  50.249              using: [NSSortDescriptor.init(key: "uid", ascending: true)]) as? [CdMessage] {
  50.250 -            XCTAssertEqual(cdOutgoingMsgs.count, numberOfMails)
  50.251 -            for m in cdOutgoingMsgs {
  50.252 +            let unsent = cdOutgoingMsgs.filter { $0.uid == 0 }
  50.253 +            XCTAssertEqual(unsent.count, numberOfMails)
  50.254 +            for m in unsent {
  50.255                  XCTAssertEqual(m.parent?.folderType, FolderType.sent)
  50.256                  XCTAssertEqual(m.uid, Int32(0))
  50.257                  XCTAssertEqual(m.sendStatus, SendStatus.none)
  50.258 @@ -471,6 +475,52 @@
  50.259          return messagesInTheQueue
  50.260      }
  50.261  
  50.262 +    // MARK: - FOLDER
  50.263 +
  50.264 +    static func determineInterestingFolders(in cdAccount: CdAccount)
  50.265 +        -> [NetworkServiceWorker.FolderInfo] {
  50.266 +        let accountInfo = AccountConnectInfo(accountID: cdAccount.objectID)
  50.267 +        let dummyConfig = NetworkService.ServiceConfig(sleepTimeInSeconds: 1,
  50.268 +                                                       parentName: #function,
  50.269 +                                                       mySelfer:
  50.270 +            DefaultMySelfer(parentName: #function,
  50.271 +                            backgrounder: nil),
  50.272 +                                                       backgrounder: nil,
  50.273 +                                                       errorPropagator: nil)
  50.274 +        let networkServiceWorker = NetworkServiceWorker(serviceConfig: dummyConfig,
  50.275 +                                                        imapConnectionDataCache: nil)
  50.276 +        return networkServiceWorker.determineInterestingFolders(accountInfo: accountInfo)
  50.277 +    }
  50.278 +
  50.279 +    static func makeFolderInteresting(folderType: FolderType, cdAccount: CdAccount) {
  50.280 +        let folder = cdFolder(ofType: folderType, in: cdAccount)
  50.281 +        folder.lastLookedAt = Date(timeInterval: -1, since: Date())
  50.282 +        Record.saveAndWait()
  50.283 +    }
  50.284 +
  50.285 +    static func cdFolder(ofType type: FolderType, in cdAccount: CdAccount) -> CdFolder {
  50.286 +        guard let folder = CdFolder.by(folderType: type, account: cdAccount, context: nil)
  50.287 +            else {
  50.288 +                fatalError()
  50.289 +        }
  50.290 +        return folder
  50.291 +    }
  50.292 +
  50.293 +    // MARK: - SERVER
  50.294 +
  50.295 +    static func setServersTrusted(forCdAccount cdAccount: CdAccount, testCase: XCTestCase) {
  50.296 +        guard let cdServers = cdAccount.servers?.allObjects as? [CdServer] else {
  50.297 +            XCTFail("No Servers")
  50.298 +            return
  50.299 +        }
  50.300 +        for server in cdServers {
  50.301 +            server.trusted = true
  50.302 +        }
  50.303 +        Record.saveAndWait()
  50.304 +    }
  50.305 +
  50.306 +    // MARK: - ERROR
  50.307 +
  50.308      class TestErrorContainer: ServiceErrorProtocol {
  50.309          var error: Error?
  50.310