diff --git a/enzevalos_iphone.xcodeproj/project.pbxproj b/enzevalos_iphone.xcodeproj/project.pbxproj index a07d7a8839c273a008cda44b2887c7bd8d63d30f..262cbaeb67b38fc344adc40c93de0e5952a7319f 100644 --- a/enzevalos_iphone.xcodeproj/project.pbxproj +++ b/enzevalos_iphone.xcodeproj/project.pbxproj @@ -46,11 +46,7 @@ 471876F7223FACA900912135 /* BobPWTEST1234.asc in Resources */ = {isa = PBXBuildFile; fileRef = 471876F5223FACA900912135 /* BobPWTEST1234.asc */; }; 471876F8223FACA900912135 /* BobWithoutPW.asc in Resources */ = {isa = PBXBuildFile; fileRef = 471876F6223FACA900912135 /* BobWithoutPW.asc */; }; 472F396E1E14F384009260FB /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 472F396D1E14F384009260FB /* CoreData.framework */; }; - 472F39861E1FA34E009260FB /* Record.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472F39851E1FA34E009260FB /* Record.swift */; }; - 472F398A1E251787009260FB /* Contact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472F39891E251787009260FB /* Contact.swift */; }; 472F398C1E2519C8009260FB /* CNContactExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472F398B1E2519C8009260FB /* CNContactExtension.swift */; }; - 472F398E1E251B8D009260FB /* MailAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472F398D1E251B8D009260FB /* MailAddress.swift */; }; - 472F39901E252470009260FB /* CNMailAddressesExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472F398F1E252470009260FB /* CNMailAddressesExtension.swift */; }; 4733B1E52527196100AB5600 /* PersistentDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4733B1E42527196100AB5600 /* PersistentDataProvider.swift */; }; 4733B202252B142C00AB5600 /* Properties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4733B201252B142B00AB5600 /* Properties.swift */; }; 4733B206252B16D100AB5600 /* FolderRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4733B205252B16D100AB5600 /* FolderRecord.swift */; }; @@ -82,8 +78,6 @@ 476761FA258A5A9E0014EE50 /* MailRecord+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 476761F8258A5A9E0014EE50 /* MailRecord+CoreDataProperties.swift */; }; 476801DB218436B600F7F259 /* Autocrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 476801DA218436B600F7F259 /* Autocrypt.swift */; }; 476801DE21846A5A00F7F259 /* OutgoingMail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 476801DD21846A5A00F7F259 /* OutgoingMail.swift */; }; - 47691A8A1ECB56D1004BCFC5 /* Mail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47691A891ECB56D1004BCFC5 /* Mail.swift */; }; - 47691A8C1ECC3EC7004BCFC5 /* EphemeralMail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47691A8B1ECC3EC7004BCFC5 /* EphemeralMail.swift */; }; 476EEF9922A872BF00BB4EF7 /* enzevalos_iphoneUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 476EEF9822A872BF00BB4EF7 /* enzevalos_iphoneUITests.swift */; }; 476EEFA122A873A800BB4EF7 /* OnboardingTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 476EEFA022A873A800BB4EF7 /* OnboardingTest.swift */; }; 476EEFA722A87B9800BB4EF7 /* accounts.json in Resources */ = {isa = PBXBuildFile; fileRef = 476EEFA622A87B9800BB4EF7 /* accounts.json */; }; @@ -230,7 +224,7 @@ A1BE3FF61E9664660040114B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1BE3FF81E9664660040114B /* InfoPlist.strings */; }; A1E5960E1FCC871F003791E9 /* researchteam.asc in Resources */ = {isa = PBXBuildFile; fileRef = A1E5960D1FCC871E003791E9 /* researchteam.asc */; }; A1EB057C1D956838008659C1 /* MailHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EB057B1D956838008659C1 /* MailHandler.swift */; }; - A1EB05881D956879008659C1 /* AddressHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EB05871D956879008659C1 /* AddressHandler.swift */; }; + A1EB05881D956879008659C1 /* ContactHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EB05871D956879008659C1 /* ContactHandler.swift */; }; A1EB05A41D956E32008659C1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1EB05A31D956E32008659C1 /* Assets.xcassets */; }; A5E303D824110F6400310264 /* smime-helpers.c in Sources */ = {isa = PBXBuildFile; fileRef = A5E303D724110F6400310264 /* smime-helpers.c */; }; AD97DFBE241F97A300C35B95 /* OnboardingIntroInfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD97DFBD241F97A300C35B95 /* OnboardingIntroInfoSection.swift */; }; @@ -327,11 +321,7 @@ 471876F5223FACA900912135 /* BobPWTEST1234.asc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = BobPWTEST1234.asc; sourceTree = "<group>"; }; 471876F6223FACA900912135 /* BobWithoutPW.asc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = BobWithoutPW.asc; sourceTree = "<group>"; }; 472F396D1E14F384009260FB /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; - 472F39851E1FA34E009260FB /* Record.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Record.swift; sourceTree = "<group>"; }; - 472F39891E251787009260FB /* Contact.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Contact.swift; sourceTree = "<group>"; }; 472F398B1E2519C8009260FB /* CNContactExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CNContactExtension.swift; sourceTree = "<group>"; }; - 472F398D1E251B8D009260FB /* MailAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MailAddress.swift; sourceTree = "<group>"; }; - 472F398F1E252470009260FB /* CNMailAddressesExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CNMailAddressesExtension.swift; sourceTree = "<group>"; }; 4733B1E42527196100AB5600 /* PersistentDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistentDataProvider.swift; sourceTree = "<group>"; }; 4733B201252B142B00AB5600 /* Properties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Properties.swift; sourceTree = "<group>"; }; 4733B205252B16D100AB5600 /* FolderRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderRecord.swift; sourceTree = "<group>"; }; @@ -473,8 +463,6 @@ 476761F8258A5A9E0014EE50 /* MailRecord+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MailRecord+CoreDataProperties.swift"; sourceTree = "<group>"; }; 476801DA218436B600F7F259 /* Autocrypt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Autocrypt.swift; sourceTree = "<group>"; }; 476801DD21846A5A00F7F259 /* OutgoingMail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingMail.swift; sourceTree = "<group>"; }; - 47691A891ECB56D1004BCFC5 /* Mail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mail.swift; sourceTree = "<group>"; }; - 47691A8B1ECC3EC7004BCFC5 /* EphemeralMail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EphemeralMail.swift; sourceTree = "<group>"; }; 476EEF9622A872BF00BB4EF7 /* enzevalos_iphoneUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = enzevalos_iphoneUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 476EEF9822A872BF00BB4EF7 /* enzevalos_iphoneUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = enzevalos_iphoneUITests.swift; sourceTree = "<group>"; }; 476EEF9A22A872BF00BB4EF7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; @@ -576,6 +564,7 @@ 47EABF2F24240BD300774A93 /* AccountSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSetupView.swift; sourceTree = "<group>"; }; 47EABF332424FD1800774A93 /* SwiftUIOnboardingUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIOnboardingUITests.swift; sourceTree = "<group>"; }; 47F0376D22A7278A0005C9DE /* accounts.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = accounts.json; sourceTree = "<group>"; }; + 47F6066425F115AF00F12B10 /* DataModel2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = DataModel2.xcdatamodel; sourceTree = "<group>"; }; 47F867DF2052B47C00AA832F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 47F867E12052B48E00AA832F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 47F867E32052B49800AA832F /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; }; @@ -619,7 +608,7 @@ A1BE3FF91E96646A0040114B /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; }; A1E5960D1FCC871E003791E9 /* researchteam.asc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = researchteam.asc; path = keys/researchteam.asc; sourceTree = "<group>"; }; A1EB057B1D956838008659C1 /* MailHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MailHandler.swift; sourceTree = "<group>"; }; - A1EB05871D956879008659C1 /* AddressHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddressHandler.swift; sourceTree = "<group>"; }; + A1EB05871D956879008659C1 /* ContactHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactHandler.swift; sourceTree = "<group>"; }; A1EB05A31D956E32008659C1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; A5E303D624110F6400310264 /* smime-helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "smime-helpers.h"; sourceTree = "<group>"; }; A5E303D724110F6400310264 /* smime-helpers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "smime-helpers.c"; sourceTree = "<group>"; }; @@ -792,17 +781,6 @@ path = PGP; sourceTree = "<group>"; }; - 472F39921E279792009260FB /* protocols */ = { - isa = PBXGroup; - children = ( - 472F398D1E251B8D009260FB /* MailAddress.swift */, - 472F39851E1FA34E009260FB /* Record.swift */, - 472F39891E251787009260FB /* Contact.swift */, - 47691A891ECB56D1004BCFC5 /* Mail.swift */, - ); - name = protocols; - sourceTree = "<group>"; - }; 4750BDB925399D7A00F6D5AB /* records */ = { isa = PBXGroup; children = ( @@ -1138,13 +1116,10 @@ children = ( 47B71AB225387AC000CA87C6 /* SearchHelper.swift */, A15D215A223BE5F4003E0CE0 /* TempAttachment.swift */, - 472F39921E279792009260FB /* protocols */, A114E4311FACB23000E40243 /* StringExtension.swift */, 4751C6ED233CA583006B2A4D /* DateExtension.swift */, - 47691A8B1ECC3EC7004BCFC5 /* EphemeralMail.swift */, 472F398B1E2519C8009260FB /* CNContactExtension.swift */, - 472F398F1E252470009260FB /* CNMailAddressesExtension.swift */, - A1EB05871D956879008659C1 /* AddressHandler.swift */, + A1EB05871D956879008659C1 /* ContactHandler.swift */, ); name = data; path = ..; @@ -1636,10 +1611,9 @@ }; buildConfigurationList = A13526701D955BDF00D3BFE1 /* Build configuration list for PBXProject "enzevalos_iphone" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, Base, de, @@ -1893,12 +1867,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 47691A8A1ECB56D1004BCFC5 /* Mail.swift in Sources */, 476801DE21846A5A00F7F259 /* OutgoingMail.swift in Sources */, 47B71AAF2538354A00CA87C6 /* OnboardingValueState.swift in Sources */, 477120A8254C289B00B28C64 /* ContactTabView.swift in Sources */, - 472F398A1E251787009260FB /* Contact.swift in Sources */, - 472F39861E1FA34E009260FB /* Record.swift in Sources */, 47BCAF2A259CD5CE0008FE4B /* KeyManagementModel.swift in Sources */, 47C09C76243B3395007F74A2 /* SmallContactListView.swift in Sources */, 47EABF272423BFDD00774A93 /* AuthenticationScreen.swift in Sources */, @@ -1920,7 +1891,6 @@ 476761FA258A5A9E0014EE50 /* MailRecord+CoreDataProperties.swift in Sources */, 476406992416B54D00C7D426 /* SearchView.swift in Sources */, 47D1302B1F7CEE6D007B14DF /* DebugSettings.swift in Sources */, - 47691A8C1ECC3EC7004BCFC5 /* EphemeralMail.swift in Sources */, A5E303D824110F6400310264 /* smime-helpers.c in Sources */, 47FA8EC3254D9E01006883D0 /* RecipientFieldModel.swift in Sources */, 4775D7A8243F0D630052F2CC /* DisplayProtocols.swift in Sources */, @@ -2004,8 +1974,7 @@ 477120C2254C676000B28C64 /* ContactView.swift in Sources */, 47EABF09241A9C8700774A93 /* AuthenticationModel.swift in Sources */, 47FA8EA8254C7E5B006883D0 /* FolderRow.swift in Sources */, - A1EB05881D956879008659C1 /* AddressHandler.swift in Sources */, - 472F398E1E251B8D009260FB /* MailAddress.swift in Sources */, + A1EB05881D956879008659C1 /* ContactHandler.swift in Sources */, 47EABF2D2423C65F00774A93 /* AuthenticationView.swift in Sources */, 47BCAF68259F48840008FE4B /* TempKeyRow.swift in Sources */, 479011492289975D0057AB04 /* NoSecIconStyleKit.swift in Sources */, @@ -2565,9 +2534,10 @@ 47FAE30C2524AA97005A1BCB /* DataModel.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + 47F6066425F115AF00F12B10 /* DataModel2.xcdatamodel */, 47FAE30D2524AA97005A1BCB /* DataModel.xcdatamodel */, ); - currentVersion = 47FAE30D2524AA97005A1BCB /* DataModel.xcdatamodel */; + currentVersion = 47F6066425F115AF00F12B10 /* DataModel2.xcdatamodel */; path = DataModel.xcdatamodeld; sourceTree = "<group>"; versionGroupType = wrapper.xcdatamodel; diff --git a/enzevalos_iphone.xcodeproj/xcshareddata/xcschemes/enzevalos_iphone.xcscheme b/enzevalos_iphone.xcodeproj/xcshareddata/xcschemes/enzevalos_iphone.xcscheme index e36c9ae501f96ec7306ae7d239a3a2f541b24756..85a049fc31b67f0e558e0b3f36d2d520a0cfcf87 100644 --- a/enzevalos_iphone.xcodeproj/xcshareddata/xcschemes/enzevalos_iphone.xcscheme +++ b/enzevalos_iphone.xcodeproj/xcshareddata/xcschemes/enzevalos_iphone.xcscheme @@ -38,6 +38,38 @@ ReferencedContainer = "container:enzevalos_iphone.xcodeproj"> </BuildableReference> </MacroExpansion> + <AdditionalOptions> + <AdditionalOption + key = "MallocGuardEdges" + value = "" + isEnabled = "YES"> + </AdditionalOption> + <AdditionalOption + key = "MallocStackLogging" + value = "" + isEnabled = "YES"> + </AdditionalOption> + <AdditionalOption + key = "NSZombieEnabled" + value = "YES" + isEnabled = "YES"> + </AdditionalOption> + <AdditionalOption + key = "DYLD_INSERT_LIBRARIES" + value = "/usr/lib/libgmalloc.dylib" + isEnabled = "YES"> + </AdditionalOption> + <AdditionalOption + key = "PrefersMallocStackLoggingLite" + value = "" + isEnabled = "YES"> + </AdditionalOption> + <AdditionalOption + key = "MallocScribble" + value = "" + isEnabled = "YES"> + </AdditionalOption> + </AdditionalOptions> <Testables> <TestableReference skipped = "NO"> @@ -73,6 +105,51 @@ <Test Identifier = "MailTest/testImportSecretKeyWithPWNoEnc()"> </Test> + <Test + Identifier = "MailTest/testK9SecureInlineMail()"> + </Test> + <Test + Identifier = "MailTest/testK9SecureMail()"> + </Test> + <Test + Identifier = "MailTest/testK9SigedInlineMail()"> + </Test> + <Test + Identifier = "MailTest/testK9SigedMail()"> + </Test> + <Test + Identifier = "MailTest/testMacEncMail()"> + </Test> + <Test + Identifier = "MailTest/testMacSecureMail()"> + </Test> + <Test + Identifier = "MailTest/testMacSigedMail()"> + </Test> + <Test + Identifier = "MailTest/testMixedMailCreation()"> + </Test> + <Test + Identifier = "MailTest/testSMIMEMailFromMac()"> + </Test> + <Test + Identifier = "MailTest/testThunderBirdSecureInlineMail()"> + </Test> + <Test + Identifier = "MailTest/testThunderbirdEncInlineMail()"> + </Test> + <Test + Identifier = "MailTest/testThunderbirdEncMail()"> + </Test> + <Test + Identifier = "MailTest/testThunderbirdSecureMail()"> + </Test> + <Test + Identifier = "MailTest/testThunderbirdSigedInlineMail()"> + </Test> + <Test + Identifier = "MailTest/testThunderbirdSigedMail()"> + </Test> </SkippedTests> </TestableReference> <TestableReference diff --git a/enzevalos_iphone/AddressHandler 2.swift b/enzevalos_iphone/AddressHandler 2.swift new file mode 100644 index 0000000000000000000000000000000000000000..c69264972c14cf9de65f8d969d590d8d7660034c --- /dev/null +++ b/enzevalos_iphone/AddressHandler 2.swift @@ -0,0 +1,329 @@ +// +// AddressHandler.swift +// mail_dynamic_icon_001 +// +// Created by jakobsbode on 14.07.16. +// // This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +import Foundation +import Contacts +import UIKit.UIImage + +class AddressHandler { + + static var addresses: [String] = [] +/* + static var freqAlgorithm: ([String]) -> [(UIImage, String, String, UIImage?, UIColor)] = { + (inserted: [String]) -> [(UIImage, String, String, UIImage?, UIColor)] in + var cons: [(UIImage, String, String, UIImage?, UIColor)] = [] + do { + + try AppDelegate.getAppDelegate().contactStore.enumerateContacts(with: CNContactFetchRequest(keysToFetch: [CNContactFormatter.descriptorForRequiredKeys(for: CNContactFormatterStyle.fullName), CNContactEmailAddressesKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor, CNContactThumbnailImageDataKey as CNKeyDescriptor]), usingBlock: { + (c: CNContact, stop) -> Void in + for email in c.emailAddresses { + let addr = email.value as String + var type: UIImage? = nil + if c.emailAddresses.count > 1 { + if email.label == "_$!<Work>!$_" { + type = UIImage(named: "work2_white")! + } else if email.label == "_$!<Home>!$_" { + type = UIImage(named: "home2_white")! + } else if email.label == "_$!<iCloud>!$_" { + //TODO: appleIcon hinzufügen + } + } + var color = c.getColor() + if c.thumbnailImageData != nil { + color = UIColor.gray //blackColor() + } + if addr == "" { + continue + } + if !inserted.contains(addr.lowercased()) { + if let name = CNContactFormatter.string(from: c, style: .fullName) { + cons.append((c.getImageOrDefault(), name, addr, type, color)) + } else { + cons.append((c.getImageOrDefault(), "NO NAME", addr, type, color)) + } + } + } + }) + } + catch { } + var list: [(UIImage, String, String, UIImage?, UIColor)] = [] + var entrys = CollectionDataDelegate.maxFrequent + if cons.count < entrys { + entrys = cons.count + } + if entrys <= 0 { + return [] + } + for i in 0...entrys - 1 { + //let index = abs(Int(arc4random())) % cons.count + let index = i % cons.count + list.append(cons[index]) + cons.remove(at: index) + } + + return list + } +// TODO + static var freqAlgorithm2: ([String]) -> [(UIImage, String, String, UIImage?, UIColor)] = { + (inserted: [String]) -> [(UIImage, String, String, UIImage?, UIColor)] in + + var cons = DataHandler.handler.folderRecords(folderPath: UserManager.backendInboxFolderPath) + var list: [(UIImage, String, String, UIImage?, UIColor)] = [] + var localInserted = inserted + + for con: KeyRecord in cons { + if list.count >= CollectionDataDelegate.maxFrequent { + break + } + var insertedEntry = false + var address = con.ezContact.getMailAddresses()[0] + for addr in con.ezContact.getMailAddresses() { + if localInserted.contains(addr.mailAddress) { + insertedEntry = true + } + if addr.hasKey { + address = addr + } + } + if !insertedEntry { + var addrType: UIImage? = nil + + if address.label.label == "_$!<Work>!$_" { + addrType = UIImage(named: "work2_white")! + } + if address.label.label == "_$!<Home>!$_" { + addrType = UIImage(named: "home2_white")! + } + if let cn = con.cnContact { + var color = cn.getColor() + if cn.thumbnailImageData != nil { + color = UIColor.gray //blackColor() + } + + var entry = (cn.getImageOrDefault(), con.ezContact.displayname!, address.mailAddress, addrType, color) + + list.append(entry) + localInserted.append(address.mailAddress) + } else { + var entry = (con.ezContact.getImageOrDefault(), con.ezContact.displayname ?? address.mailAddress, address.mailAddress, addrType, con.ezContact.getColor()) + list.append(entry) + localInserted.append(address.mailAddress) + } + } + } + + + + return list + } */ + + static func getContact(_ name: String) -> [CNContact] { + if name == "" { + return [] + } + /*TODO AppDelegate.getAppDelegate().requestForAccess({ access in }) + let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts) + if authorizationStatus == CNAuthorizationStatus.authorized { + do { + let conList = try AppDelegate.getAppDelegate().contactStore.unifiedContacts(matching: CNContact.predicateForContacts(matchingName: name), keysToFetch: [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactEmailAddressesKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor, CNContactThumbnailImageDataKey as CNKeyDescriptor]) + return conList + } catch { + print("exception in contacts get for name: \(name)") + } + } else { + print("no Access!") + } */ + return [] + } + + static func getContactByID(_ identifier: String) -> [CNContact] { + /* AppDelegate.getAppDelegate().requestForAccess({ access in }) + let ids = [identifier] + let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts) + if authorizationStatus == CNAuthorizationStatus.authorized { + do { + let conList = try AppDelegate.getAppDelegate().contactStore.unifiedContacts(matching: CNContact.predicateForContacts(withIdentifiers: ids), keysToFetch: [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactEmailAddressesKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor, CNContactThumbnailImageDataKey as CNKeyDescriptor]) + return conList + } catch { + print("exception in contacts") + } + } else { + print("no Access!") + } */ + return [] + } + + /* [insertedEmail] -> [(contactImage, name, address, emailLabelImage, backgroundcolor)] */ + /* TODO + static func frequentAddresses (_ inserted: [String]) -> [(UIImage, String, String, UIImage?, UIColor)] { + let cons = DataHandler.handler.folderRecords(folderPath: UserManager.backendInboxFolderPath) + var list: [(UIImage, String, String, UIImage?, UIColor)] = [] + var localInserted = inserted + + for con: KeyRecord in cons { + if list.count >= CollectionDataDelegate.maxFrequent { + break + } + var insertedEntry = false + if con.addressNames.isEmpty { + continue + } + var address = con.addresses.first! + if let a = con.ezContact.getMailAddresses().first { + address = a + } + for addr in con.ezContact.getMailAddresses() { + if localInserted.contains(addr.mailAddress) { + insertedEntry = true + } + if addr.hasKey { + address = addr + } + } + if !insertedEntry { + var addrType: UIImage? = nil + + if address.label.label == "_$!<Work>!$_" { + addrType = UIImage(named: "work2_white")! + } + if address.label.label == "_$!<Home>!$_" { + addrType = UIImage(named: "home2_white")! + } + if let cn = con.cnContact { + var color = cn.getColor() + if cn.thumbnailImageData != nil { + color = UIColor.gray //blackColor() + } + let entry = (cn.getImageOrDefault(), con.ezContact.displayname!, address.mailAddress, addrType, color) + + list.append(entry) + localInserted.append(address.mailAddress) + } else { + let entry = (con.ezContact.getImageOrDefault(), con.ezContact.displayname ?? address.mailAddress, address.mailAddress, addrType, con.ezContact.getColor()) + list.append(entry) + localInserted.append(address.mailAddress) + } + } + } + + return list + } +*/ + static func findContact(_ econtact: Contact) -> [CNContact] { + var result = [CNContact]() + if let identifier = econtact.cnContact?.identifier { + // 1. Look up identifier string + result = getContactByID(identifier) + } + if result.count == 0 { + let name = econtact.name.trimmingCharacters(in: .decimalDigits) + // 2. look for name + let query = getContact(name) + for res in query { + if (proveMatching(res, addresses: econtact.getMailAddresses())) { + result.append(res) + } + } + } + if result.count == 0 { + // 3. look for mail addresses + result = contactByEmail(econtact.getMailAddresses()) + } + return result + } + + static func proveMatching(_ result: CNContact, addresses: [MailAddress]) -> Bool { + for email in result.emailAddresses { + for adr in addresses { + let adrRest = email.value as String + if adrRest.lowercased() == adr.email.lowercased() { + return true + } + } + } + return false + } + + static func contactByEmail(_ mailaddreses: [MailAddress]) -> [CNContact] { + var contacts: [CNContact] = [] + + let fetchRequest = CNContactFetchRequest(keysToFetch: [CNContactFormatter.descriptorForRequiredKeys(for: CNContactFormatterStyle.fullName), CNContactEmailAddressesKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor, CNContactThumbnailImageDataKey as CNKeyDescriptor]) +/*TODO + do { + try AppDelegate.getAppDelegate().contactStore.enumerateContacts(with: fetchRequest, usingBlock: { (contact, _) in + for addr in mailaddreses { + let contains = Set(contact.emailAddresses.map({ $0.value as String })).contains(addr.email) + if contains { + contacts.append(contact) + } + } + }) + } catch { + print("Problem while accessing contacts by email") + } +*/ + return contacts + } + + + static func updateCNContacts(save: Bool = true) { + let enzContacts = [Contact]() // TODODataHandler.handler.getContacts() + + for contact in enzContacts { + let contacts = findContact(contact) + if contact.cnContact == nil { + if contacts.count > 0 { + // TODO contact.cnContact = contacts.first?.identifier + } + } + else if contacts.count > 0 && contact.cnContact != nil { + //TODO contact.cnContact = nil + } + else if contacts.count > 0 { + let cnContact = contacts.first + if let addresses = cnContact?.getMailAddresses() { + var hasSame = false + for adr1 in contact.getMailAddresses() { + var found = false + for adr2 in addresses { + if adr1.email == adr2.email { + found = true + break + } + } + if found { + hasSame = true + } + } + if !hasSame { + //TODO contact.cnContact = nil + } + } + else { + //TODO contact.cnContact = nil + } + + + } + } + if save { + //TODO DataHandler.handler.save(during: "updateCNContacts") + } + } +} diff --git a/enzevalos_iphone/AddressHandler.swift b/enzevalos_iphone/AddressHandler.swift index c69264972c14cf9de65f8d969d590d8d7660034c..456b2450e9598932fc68cf65712b8fbedfb102a0 100644 --- a/enzevalos_iphone/AddressHandler.swift +++ b/enzevalos_iphone/AddressHandler.swift @@ -21,7 +21,8 @@ import Foundation import Contacts import UIKit.UIImage -class AddressHandler { +class ContactHandler { + static let contactStore = CNContactStore() static var addresses: [String] = [] /* diff --git a/enzevalos_iphone/CNContactExtension.swift b/enzevalos_iphone/CNContactExtension.swift index 3e1ca87b0d4b2868c48328b00478d5ddbf830f7e..32a227a323193fa6aabe6f8d9dddd5b2edfa1e1c 100644 --- a/enzevalos_iphone/CNContactExtension.swift +++ b/enzevalos_iphone/CNContactExtension.swift @@ -21,15 +21,10 @@ import Foundation import Contacts -extension CNContact: Contact { - - public var cnContact: CNContact? { - return self - } +extension CNContact { public var name: String { - var name: String - name = String() + var name = "" if self.givenName.count > 0 { name += self.givenName } @@ -46,12 +41,4 @@ extension CNContact: Contact { return name } - public func getMailAddresses() -> [MailAddress] { - var adr: [MailAddress] = [] - for a in self.emailAddresses { - adr.append(CNMailAddressExtension(addr: a.value)) - } - return adr - } - } diff --git a/enzevalos_iphone/CNMailAddressesExtension.swift b/enzevalos_iphone/CNMailAddressesExtension.swift deleted file mode 100644 index a6af690f654bbbc6fbcacf972b508c186582ef4e..0000000000000000000000000000000000000000 --- a/enzevalos_iphone/CNMailAddressesExtension.swift +++ /dev/null @@ -1,80 +0,0 @@ -// -// CNMailAddressesExtension.swift -// enzevalos_iphone -// -// Created by Oliver Wiese on 10/01/17. -// Copyright © 2018 fu-berlin. -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -// - -import Foundation -import Contacts - -open class CNMailAddressExtension: MailAddress { - public var isUser: Bool { - get { - if let userAdr = UserManager.loadUserValue(.userAddr) as? String { - return self.email.lowercased() == userAdr.lowercased() - } - return false - } - } - - public var primaryKey: PublicKeyRecord? - - public var publicKeys: Set<PublicKeyRecord> - - - open var email: String { - get { - return label.value as String - } - } - open var label: CNLabeledValue<NSString> - - open var prefEnc: AutocryptState { - get { - return AutocryptState.NOAUTOCRYPT - } - set { - } - } - public var hasPublicKey: Bool { - get { - return false - } - } - - - open var Key: PublicKeyRecord? { - get { - return nil - } - } - - open var contact: Contact? { - get { - return nil - } - } - - init(addr: CNLabeledValue<NSString>) { - self.label = addr - self.publicKeys = Set<PublicKeyRecord>() - } - - convenience init(addr: NSString) { - self.init(addr: CNLabeledValue(label: CNLabelOther, value: addr)) - } -} diff --git a/enzevalos_iphone/Contact.swift b/enzevalos_iphone/Contact.swift deleted file mode 100644 index bb6c6f20504caae8aed29442695e5460516fc8ee..0000000000000000000000000000000000000000 --- a/enzevalos_iphone/Contact.swift +++ /dev/null @@ -1,98 +0,0 @@ -// -// Contact.swift -// enzevalos_iphone -// -// Created by Oliver Wiese on 10/01/17. -// Copyright © 2018 fu-berlin. -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -// - -import Foundation -import Contacts -import UIKit - - -public protocol Contact { - var name: String { get } - var cnContact: CNContact? { get } - func getMailAddresses() -> [MailAddress] -} - -extension Contact { - public static func makeImageFromName(_ name: String, color: UIColor) -> UIImage { - var text: NSAttributedString - var tag = String() - if name.count > 0 { - let seperated = name.components(separatedBy: " ") - tag = seperated.map({ if let a = $0.first { return "\(a)" }; return "" }).joined() - } - - text = NSAttributedString(string: tag.uppercased(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 32.2)]) - - var myBounds = CGRect() - myBounds.size.width = 70 - myBounds.size.height = 70 - UIGraphicsBeginImageContextWithOptions(myBounds.size, false, 2) //try 200 here - - let context = UIGraphicsGetCurrentContext() - - // - // Clip context to a circle - // - let path = CGPath(ellipseIn: myBounds, transform: nil); - context!.addPath(path); - context!.clip(); - - // - // Fill background of context - // - context!.setFillColor(color.cgColor) - context!.fill(CGRect(x: 0, y: 0, width: myBounds.size.width, height: myBounds.size.height)); - - - // - // Draw text in the context - // - let textSize = text.size() - - text.draw(in: CGRect(x: myBounds.size.width / 2 - textSize.width / 2, y: myBounds.size.height / 2 - textSize.height / 2, width: textSize.width, height: textSize.height)) - - - let snapshot = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return snapshot! - - } - - func getImageOrDefault() -> UIImage { - if let contact = cnContact { - if (contact.thumbnailImageData != nil) { - return UIImage(data: contact.thumbnailImageData!)! - } - } - return Self.makeImageFromName(self.name, color: self.getColor()) - } - - func getColor() -> UIColor { - - - //enzCon.color = UIColor(hue: CGFloat(arc4random()) / CGFloat(UINT32_MAX), saturation: 1, brightness: 0.75, alpha: 1) - // TODO: How to calculate the color? - // Overflow?! - let prim = 653 - - let hash = (abs(self.name.hash)) % prim - return UIColor(hue: CGFloat(hash) / CGFloat(prim), saturation: 1, brightness: 0.75, alpha: 1) - } -} diff --git a/enzevalos_iphone/ContactHandler.swift b/enzevalos_iphone/ContactHandler.swift new file mode 100644 index 0000000000000000000000000000000000000000..9366e2f0b047b9ef7ad2490af920d459e70d4f0d --- /dev/null +++ b/enzevalos_iphone/ContactHandler.swift @@ -0,0 +1,160 @@ +// +// AddressHandler.swift +// mail_dynamic_icon_001 +// +// Created by jakobsbode on 14.07.16. +// // This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +import Foundation +import Contacts +import SwiftUI + + +// TODO: Mark displayname as from CNContact +class ContactHandler { + private let store = CNContactStore() + static let handler = ContactHandler() + let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactNicknameKey, CNContactIdentifierKey, CNContactImageDataKey] as [CNKeyDescriptor] + + + func findContactBy(email: String) -> [CNContact] { + let predicate = CNContact.predicateForContacts(matchingEmailAddress: email) + if let contacts = try? store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch) { + return contacts + } + return [] + } + + private func findContactsBy(ids: [String]) -> [CNContact] { + let predicate = CNContact.predicateForContacts(withIdentifiers: ids) + if let contacts = try? store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch) { + return contacts + } + return [] + } + + func findContacts(update: Bool) { + // TODO: Move to background thread? + if update, let frp = try? PersitentDataProvider.dataProvider.lookUp(entityName: AddressRecord.entityName, sorting: ["email": true], equalPredicates: nil, differentPredicates: ["phoneBookID": "nil"], inPredicates: nil, propertiesToFetch: ["email", "phoneBookID", "displayname", "image"]), let addresses = frp.fetchedObjects as? [AddressRecord]{ // ["phoneBookID": "nil"] + let contacts = self.findContactsBy(ids: addresses.map({($0.phoneBookID ?? "")})) + for addr in addresses { + var hit = false + for c in contacts { + if c.identifier == addr.phoneBookID { + hit = true + if c.nickname.count > 0 && c.nickname != addr.displayname { + addr.displayname = c.nickname + } else if c.name.count > 0 && c.name != addr.displayname { + addr.displayname = c.name + } + if c.imageData != addr.image { + addr.image = c.imageData + } + } + } + if hit == false { + addr.displayname = nil + addr.image = nil + addr.phoneBookID = nil + } + } + do { + try PersitentDataProvider.dataProvider.save(taskContext: frp.managedObjectContext) + } catch { + print("Can not store updates!") + } + } + + if let frp = try? PersitentDataProvider.dataProvider.lookUp(entityName: AddressRecord.entityName, sorting: ["email": true], equalPredicates: ["phoneBookID": "nil"], differentPredicates: nil, inPredicates: nil, propertiesToFetch: ["email", "phoneBookID", "displayname", "image"]), let addresses = frp.fetchedObjects as? [AddressRecord]{ // ["phoneBookID": "nil"] + for addr in addresses { + if let contact = self.findContactBy(email: addr.email).first { + addr.phoneBookID = contact.identifier + if contact.nickname.count > 0 { + addr.displayname = contact.nickname + } else if contact.name.count > 0 { + addr.displayname = contact.name + } + if contact.isKeyAvailable("CNContactImageDataKey"), let data = contact.imageData { + addr.image = data + } + } + } + do { + try PersitentDataProvider.dataProvider.save(taskContext: frp.managedObjectContext) + } catch { + print("Can not store updates!") + } + } + } + + public static func makeImageFromName(_ name: String, color: UIColor) -> UIImage { + var text: NSAttributedString + var tag = String() + if name.count > 0 { + let seperated = name.components(separatedBy: " ") + tag = seperated.map({ if let a = $0.first { return "\(a)" }; return "" }).joined() + } + + text = NSAttributedString(string: tag.uppercased(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 32.2)]) + + var myBounds = CGRect() + myBounds.size.width = 70 + myBounds.size.height = 70 + UIGraphicsBeginImageContextWithOptions(myBounds.size, false, 2) //try 200 here + + let context = UIGraphicsGetCurrentContext() + + // + // Clip context to a circle + // + let path = CGPath(ellipseIn: myBounds, transform: nil); + context!.addPath(path); + context!.clip(); + + // + // Fill background of context + // + context!.setFillColor(color.cgColor) + context!.fill(CGRect(x: 0, y: 0, width: myBounds.size.width, height: myBounds.size.height)); + + + // + // Draw text in the context + // + let textSize = text.size() + + text.draw(in: CGRect(x: myBounds.size.width / 2 - textSize.width / 2, y: myBounds.size.height / 2 - textSize.height / 2, width: textSize.width, height: textSize.height)) + + + let snapshot = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return snapshot! + + } + + static func getImageOrDefault(addr: AddressRecord) -> Image { + if let imageData = addr.image, let image = UIImage(data: imageData) { + return Image(uiImage: image) + } + return Image(uiImage: makeImageFromName(addr.name, color: getColor(name: addr.name) )) + } + + static func getColor(name: String) -> UIColor { + let prim = 653 + + let hash = (abs(name.hash)) % prim + return UIColor(hue: CGFloat(hash) / CGFloat(prim), saturation: 1, brightness: 0.75, alpha: 1) + } +} diff --git a/enzevalos_iphone/CryptoObject.swift b/enzevalos_iphone/CryptoObject.swift index e74ef3f35d1633b5bacb89e6cd2c10df9dc5c0e4..14050b27bd7645bc65b8822da43b37c3643841b5 100644 --- a/enzevalos_iphone/CryptoObject.swift +++ b/enzevalos_iphone/CryptoObject.swift @@ -118,7 +118,8 @@ public class CryptoObject { let decryptedData: Data? let signatureState: SignatureState var encryptionState: EncryptionState - let signKey: String? + let signKey: String? // TODO Remove + // TODO Add decryption keys let signedKeys: [String] let encType: CryptoScheme let passcode: String? diff --git a/enzevalos_iphone/EphemeralMail.swift b/enzevalos_iphone/EphemeralMail.swift deleted file mode 100644 index 01cd9263fb9c437908151a68bb3076fe7cc65caa..0000000000000000000000000000000000000000 --- a/enzevalos_iphone/EphemeralMail.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// EphemeralMail.swift -// enzevalos_iphone -// -// Created by Oliver Wiese on 17/05/17. -// Copyright © 2018 fu-berlin. -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -// - -import Foundation - -open class EphemeralMail: Mail { - - public var cc: NSSet? - public var bcc: NSSet? - public var to: NSSet - public var date: Date - public var subject: String? - public var body: String? - public var uid: UInt64 - public var predecessor: MailRecord? - - public init(to: NSSet = [], cc: NSSet = [], bcc: NSSet = [], date: Date = Date(), subject: String? = nil, body: String? = ""/*UserManager.loadUserSignature()*/, uid: UInt64 = 0, predecessor: MailRecord? = nil) { - self.cc = cc - self.bcc = bcc - self.to = to - if let body = body { - self.body = body - } - else { - self.body = "" - } - self.date = date - self.subject = subject - self.uid = uid - self.predecessor = predecessor - } -} diff --git a/enzevalos_iphone/Logger.swift b/enzevalos_iphone/Logger.swift index c03a34f2457279e6afdc886946f479f97deb4b2c..e1f92db0e6c4cf3c774b76471c4c32b64b67a461 100644 --- a/enzevalos_iphone/Logger.swift +++ b/enzevalos_iphone/Logger.swift @@ -173,17 +173,6 @@ class Logger { sendCheck() } - static func log(sendViewOpen mail: EphemeralMail?) { - if !logging { - return - } - } - - static func log(sendViewClose mail: EphemeralMail?) { - if !logging { - return - } - } /* static func log(createDraft to: [Mail_Address?], cc: [Mail_Address?], bcc: [Mail_Address?], subject: String, bodyLength: Int, isEncrypted: Bool, isSigned: Bool, myKeyID: String) { if !logging { @@ -297,7 +286,7 @@ class Logger { sendCheck() } - static func log(search nrOfTrays: Int, category: Int, opened: String, keyRecordMailList: [MailAddress]? = nil) { + static func log(search nrOfTrays: Int, category: Int, opened: String, keyRecordMailList: [AddressRecord]? = nil) { guard logging else { return } diff --git a/enzevalos_iphone/LoggerDetail.swift b/enzevalos_iphone/LoggerDetail.swift index 819e43626264cb2895038fe692e82c588de2be5c..823eae8b080e3b4c83d8d25c5c3c1078090a97ab 100644 --- a/enzevalos_iphone/LoggerDetail.swift +++ b/enzevalos_iphone/LoggerDetail.swift @@ -271,36 +271,6 @@ class LoggerDetail { sendCheck() } - - static func log(sendViewOpen mail: EphemeralMail?) { - if !logging { - return - } - - var event = plainLogDict() - event["type"] = LoggingEventType.sendViewOpen.rawValue - if let mail = mail { - event = extract(from: mail, event: event) - } - - saveToDisk(json: dictToJSON(fields: event)) - sendCheck() - } - - static func log(sendViewClose mail: EphemeralMail?) { - if !logging { - return - } - - var event = plainLogDict() - event["type"] = LoggingEventType.sendViewClose.rawValue - if let mail = mail { - event = extract(from: mail, event: event) - } - - saveToDisk(json: dictToJSON(fields: event)) - sendCheck() - } @@ -377,7 +347,7 @@ class LoggerDetail { "searchedInMailList" User searched in the ListView. - keyRecordMailList: Array of MailAddresses that identify the KeyRecord in which the user searched. Nil when not in MailListView. */ - static func log(search nrOfTrays: Int, category: Int, opened: String, keyRecordMailList: [MailAddress]? = nil) { + static func log(search nrOfTrays: Int, category: Int, opened: String, keyRecordMailList: [AddressRecord]? = nil) { guard logging else { return } @@ -424,26 +394,6 @@ class LoggerDetail { return event } - static fileprivate func extract(from mail: EphemeralMail, event: [String: Any]) -> [String: Any] { - var event = event - event["to"] = LoggerDetail.resolve(mailAddresses: mail.to) - event["cc"] = LoggerDetail.resolve(mailAddresses: mail.cc ?? NSSet()) - event["bcc"] = LoggerDetail.resolve(mailAddresses: mail.bcc ?? NSSet()) - event["communicationState"] = LoggerDetail.communicationState(subject: mail.subject ?? "") - event["specialMail"] = LoggerDetail.specialMail(subject: mail.subject ?? "") - event["bodyLength"] = (mail.body)?.count - //TODO: - //event["signingKeyID"] = Logger.resolve(keyID: signingKeyID) - //event["myKeyID"] = Logger.resolve(keyID: myKeyID) - - - - //event["secureAddresses"] = secureAddresses //could mean the addresses, in this mail we have a key for - //event["encryptedForKeyIDs"] = Logger.resolve(keyIDs: encryptedForKeyIDs) - - return event - } - static func communicationState(subject: String) -> String { if subject == "" { return "" @@ -499,7 +449,7 @@ class LoggerDetail { } //get an pseudonym for a mailAddress - static func resolve(mailAddress: MailAddress) -> String { + static func resolve(mailAddress: AddressRecord) -> String { if mailAddress.isUser { return "self" } @@ -510,10 +460,8 @@ class LoggerDetail { static func resolve(mailAddresses: NSSet) -> [String] { var result: [String] = [] for addr in mailAddresses { - if let addr = addr as? MailAddress { + if let addr = addr as? AddressRecord { result.append(resolve(mailAddress: addr)) - } else if addr is CNMailAddressExtension { - result.append("CNMailAddress") } else { result.append("unknownMailAddressType") } @@ -521,7 +469,7 @@ class LoggerDetail { return result } - static func resolve(mailAddresses: [MailAddress]) -> [String] { + static func resolve(mailAddresses: [AddressRecord]) -> [String] { var result: [String] = [] for addr in mailAddresses { result.append(resolve(mailAddress: addr)) diff --git a/enzevalos_iphone/Mail.swift b/enzevalos_iphone/Mail.swift deleted file mode 100644 index 306e18593d74aa2cf01acabfb35c0602c34550a4..0000000000000000000000000000000000000000 --- a/enzevalos_iphone/Mail.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// Mail.swift -// enzevalos_iphone -// -// Created by Oliver Wiese on 16/05/17. -// Copyright © 2018 fu-berlin. -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -// - -import Foundation - - -public protocol Mail: Comparable { - - var cc: NSSet? { get } - var bcc: NSSet? { get } - var to: NSSet { get } - var date: Date { get } - var subject: String? { get } - var body: String? { get } - var uid: UInt64 { get } - var predecessor: MailRecord? { get } -} - -public func == <T: Mail> (lhs: T, rhs: T) -> Bool { - return lhs.date == rhs.date && lhs.uid == rhs.uid - //TODO: update see: https://www.limilabs.com/blog/unique-id-in-imap-protocol -} - -public func << T: Mail > (lhs: T, rhs: T) -> Bool { - return lhs.date > rhs.date -} diff --git a/enzevalos_iphone/MailAddress.swift b/enzevalos_iphone/MailAddress.swift deleted file mode 100644 index e0581e6e8a37219f678f804402bcdcc5f20f11ea..0000000000000000000000000000000000000000 --- a/enzevalos_iphone/MailAddress.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// MailAddress.swift -// enzevalos_iphone -// -// Created by Oliver Wiese on 10/01/17. -// Copyright © 2018 fu-berlin. -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -// - -import Foundation -import Contacts - -public enum AutocryptState { - case MUTUAL - case GOSSIP - case NOPREFERENCE - case RESET - case NOAUTOCRYPT - - var name: String{ - get{ - switch self { - case .MUTUAL: - return "mutual" - case .NOPREFERENCE: - return "nopreference" - default: - return "" - } - } - } - - - static func find(i: Int) -> AutocryptState { - switch i { - case 0: - return AutocryptState.MUTUAL - case 1: - return AutocryptState.GOSSIP - case 2: - return AutocryptState.NOPREFERENCE - case 3: - return AutocryptState.RESET - case 4: - return AutocryptState.NOAUTOCRYPT - default: - return AutocryptState.NOAUTOCRYPT - } - } - - func canEnc() -> Bool { - - switch self { - case AutocryptState.MUTUAL: - return true - case AutocryptState.GOSSIP: - return true - case AutocryptState.RESET: - return true - default: - return false - } - } - func asInt() -> Int16 { - switch self { - case AutocryptState.MUTUAL: - return 0 - case AutocryptState.GOSSIP: - return 1 - case AutocryptState.NOPREFERENCE: - return 2 - case AutocryptState.RESET: - return 3 - case AutocryptState.NOAUTOCRYPT: - return 4 - } - } - -} - -/* - This protocol is for the backend. Handling email addresses to store, send and fetch them from the email server. - */ -public protocol MailAddress { - var email: String { get } - var label: CNLabeledValue<NSString> { get } //FIXME: ist der NSString hier wirklich richtig? (http://stackoverflow.com/questions/39648830/how-to-add-new-email-to-cnmutablecontact-in-swift-3) - var hasPublicKey: Bool { get } - var isUser: Bool {get } - - var primaryKey: PublicKeyRecord? { get } - var publicKeys: Set<PublicKeyRecord> { get } - var contact: Contact? { get } -} - - -extension MailAddress { - var mcoAddress: MCOAddress { - get { - let name = self.contact?.name ?? email - return MCOAddress.init(displayName: name, mailbox: email) - } - } -} diff --git a/enzevalos_iphone/MailComparison.swift b/enzevalos_iphone/MailComparison.swift index 1ebc31f8c997155f8fdb966f76f3003b3994eb13..ccddbf54a1ab42ddd1b2d687fb77360aa4382c63 100644 --- a/enzevalos_iphone/MailComparison.swift +++ b/enzevalos_iphone/MailComparison.swift @@ -68,22 +68,21 @@ extension String { looks if the mail address of the sender is already known ***/ public func compareSenderToContacts() -> ResultCompareSenderToContacts { + guard let inboxMails = PersitentDataProvider.dataProvider.fetchedMailResultsController.fetchedObjects else { + let contacts = ContactHandler().findContactBy(email: self) + if contacts.count > 0 { + return ResultCompareSenderToContacts.isContact + } + return ResultCompareSenderToContacts.Unknown + } // Case 1: Is the address of the sender known through previous mails? - let inboxMails = [MailRecord]()//datahandler.getAllPersistentMails() if countMatches(self, inboxMails) >= 2{ return ResultCompareSenderToContacts.isSender } // Case 2: Is the address of the sender in the address book? - let enzContacts = [AddressRecord]()// datahandler.getContacts() - for contact in enzContacts { - let contMailAddr = contact.cnContact?.getMailAddresses() - if contMailAddr != nil{ - for mAddr in contMailAddr!{ - if mAddr.email == self { - return ResultCompareSenderToContacts.isContact - } - } - } + let contacts = ContactHandler().findContactBy(email: self) + if contacts.count > 0 { + return ResultCompareSenderToContacts.isContact } // Case 3: The identity of an mail address is known, but not the (sub-)domain let foundIdentity = getIdentityWithDifferentDomain(self, inboxMails: inboxMails) diff --git a/enzevalos_iphone/New Group/Mailbot.swift b/enzevalos_iphone/New Group/Mailbot.swift index 5fda840583f13927d8d8b26c6ad320a25908e990..7ed2e4effdd5ef3dc8c5cd3bb637af5776927a64 100644 --- a/enzevalos_iphone/New Group/Mailbot.swift +++ b/enzevalos_iphone/New Group/Mailbot.swift @@ -113,7 +113,7 @@ class Mailbot { encryptionState: EncryptionState.ValidedEncryptedWithCurrentKey.rawValue, signatureKey: pkproperty, decryptionKey: nil, - autocryptHeaderKey: nil, + autocryptHeaderKey: [], attachedPublicKeys: [], attachedSecretKeys: []) PersistentDataProvider.dataProvider.importNewData(from: [mail], completionHandler: {error in }) diff --git a/enzevalos_iphone/OutgoingMail.swift b/enzevalos_iphone/OutgoingMail.swift index 207c0fb69283d14e1940174b47c36bf8857e443b..5227d034aed580274f7d0590ba5d6681bdec5d32 100644 --- a/enzevalos_iphone/OutgoingMail.swift +++ b/enzevalos_iphone/OutgoingMail.swift @@ -164,7 +164,7 @@ class OutgoingMail { let encState = self.cryptoObject?.encryptionState.rawValue ?? EncryptionState.NoEncryption.rawValue let from = AddressProperties(email: self.sender.mailbox, name: self.sender.displayName) - let m = MailProperties(messageID: UUID().uuidString, subject: subject, date: Date(), flags: 0, from: from, to: to, cc: cc , bcc: bcc, folder: OutgoingMail.OutgoingFolder, body: body, attachments: attachmentProperties, signatureState: sigState, encryptionState: encState, signatureKey: nil, decryptionKey: nil, autocryptHeaderKey: nil, attachedPublicKeys: [], attachedSecretKeys: []) + let m = MailProperties(messageID: UUID().uuidString, subject: subject, date: Date(), flags: 0, from: from, to: to, cc: cc , bcc: bcc, folder: OutgoingMail.OutgoingFolder, body: body, attachments: attachmentProperties, signatureState: sigState, encryptionState: encState, signatureKey: nil, decryptionKey: nil, autocryptHeaderKey: [], attachedPublicKeys: [], attachedSecretKeys: []) LetterboxModel.instance.dataProvider.importNewData(from: [m], completionHandler: { _ in }) // TODO Fix crypto stuff (keys etc.) } @@ -224,11 +224,11 @@ class OutgoingMail { return false } // TODO let fromLogging: MailAddress = DataHandler.handler.getMailAddress(useraddr, temporary: false) as! Mail_Address - let toLogging: [MailAddress] = [] - let ccLogging: [MailAddress] = [] - let bccLogging: [MailAddress] = [] + let toLogging: [AddressRecord] = [] + let ccLogging: [AddressRecord] = [] + let bccLogging: [AddressRecord] = [] let secureAddrsInString = encAddresses.map { $0.mailbox } - var secureAddresses: [MailAddress] = [] + var secureAddresses: [AddressRecord] = [] for addr in toLogging { for sec in secureAddrsInString { if addr.email == sec { @@ -341,11 +341,8 @@ class OutgoingMail { headerString += "BCC: " + bccs + "\r\n" } headerString += "X-Mailer: Letterbox \r\n" - if autocrypt { - Autocrypt.addAutocryptHeader(builder) - if let autocrypt = builder.header.extraHeaderValue(forName: Autocrypt.AUTOCRYPTHEADER) { - //headerString += Autocrypt.AUTOCRYPTHEADER + ": " + autocrypt + "\r\n" - } + if let header = Autocrypt.customAutocryptHeader { + headerString += header } if let msgID = header?.messageID { headerString += "Message-ID: " + msgID + "\r\n" @@ -476,7 +473,7 @@ class OutgoingMail { }) } - private static func mapToMCOAddresses(addr: [MailAddress]) -> [MCOAddress]{ + private static func mapToMCOAddresses(addr: [AddressRecord]) -> [MCOAddress]{ return addr.map{$0.mcoAddress} } diff --git a/enzevalos_iphone/PGP/Autocrypt.swift b/enzevalos_iphone/PGP/Autocrypt.swift index 46ee6876cc6a78a9af2f4abdbe9019f33549dea2..a1c1b2f38b38e6e2875e90f766a3302721f261bd 100644 --- a/enzevalos_iphone/PGP/Autocrypt.swift +++ b/enzevalos_iphone/PGP/Autocrypt.swift @@ -11,6 +11,75 @@ import Foundation // See: https://autocrypt.org/level1.html +public enum AutocryptState { + case MUTUAL + case GOSSIP + case NOPREFERENCE + case RESET + case NOAUTOCRYPT + + var name: String{ + get{ + switch self { + case .MUTUAL: + return "mutual" + case .NOPREFERENCE: + return "nopreference" + default: + return "" + } + } + } + + + static func find(i: Int) -> AutocryptState { + switch i { + case 0: + return AutocryptState.MUTUAL + case 1: + return AutocryptState.GOSSIP + case 2: + return AutocryptState.NOPREFERENCE + case 3: + return AutocryptState.RESET + case 4: + return AutocryptState.NOAUTOCRYPT + default: + return AutocryptState.NOAUTOCRYPT + } + } + + func canEnc() -> Bool { + + switch self { + case AutocryptState.MUTUAL: + return true + case AutocryptState.GOSSIP: + return true + case AutocryptState.RESET: + return true + default: + return false + } + } + func asInt() -> Int16 { + switch self { + case AutocryptState.MUTUAL: + return 0 + case AutocryptState.GOSSIP: + return 1 + case AutocryptState.NOPREFERENCE: + return 2 + case AutocryptState.RESET: + return 3 + case AutocryptState.NOAUTOCRYPT: + return 4 + } + } + +} + + class Autocrypt { static let ENFORCEENCRYPTION = true static let AUTOCRYPTHEADER = "Autocrypt" @@ -32,7 +101,6 @@ class Autocrypt { setPrefer_encryption(prefer_encryption) } - convenience init(header: MCOMessageHeader) { var autocrypt = header.extraHeaderValue(forName: Autocrypt.AUTOCRYPTHEADER) var field: [String] @@ -121,13 +189,22 @@ class Autocrypt { } } + /// Use this function to create an autocrypt header for manuel header creation. + /// - returns: Autcrypt: Addr = ...; key=... + static var customAutocryptHeader: String? { + guard let autocrypt = autocryptHeader else { + return nil + } + return Autocrypt.AUTOCRYPTHEADER + ": " + autocrypt + "\r\n" + } + static func addAutocryptHeader(_ builder: MCOMessageBuilder) { if let autocrypt = Autocrypt.autocryptHeader { builder.header.setExtraHeaderValue(autocrypt, forName: AUTOCRYPTHEADER) } } - static func recommandateEncryption (receiver: MailAddress) -> (hasAutocrypt: Bool, recommandEnc: Bool){ + static func recommandateEncryption (receiver: AddressRecord) -> (hasAutocrypt: Bool, recommandEnc: Bool){ if receiver.hasPublicKey, let key = receiver.primaryKey { if key.preferEncryption == AutocryptState.NOAUTOCRYPT.asInt() { return (false, ENFORCEENCRYPTION) diff --git a/enzevalos_iphone/PGP/SwiftPGP.swift b/enzevalos_iphone/PGP/SwiftPGP.swift index 374876e2ee758cd9d02cdf539483d64677431c14..1a7ece176f1e3b3465659de64db06b3f9b839acb 100644 --- a/enzevalos_iphone/PGP/SwiftPGP.swift +++ b/enzevalos_iphone/PGP/SwiftPGP.swift @@ -349,7 +349,7 @@ class SwiftPGP: Encryption { func exportKey(id: String, isSecretkey isSecretKey: Bool, autocrypt: Bool, newPasscode: Bool = false) -> String?{ if let key = exportKeyData(id: id, isSecretkey: isSecretKey){ if !isSecretKey && autocrypt{ - return key.base64EncodedString(options: .init(arrayLiteral: .lineLength76Characters, .endLineWithLineFeed)) + return key.base64EncodedString() } else{ var armoredKey : String @@ -368,10 +368,12 @@ class SwiftPGP: Encryption { } exportPwKeyChain[id] = passcode if let message = armoredKey.data(using: .utf8) { + /* if let cipher = try? ObjectivePGP.symmetricEncrypt(message, signWith: nil, encryptionKey: passcode, passphrase: passcode, armored: false){ let armorMessage = Armor.armored(cipher, as: PGPArmorType.message) return armorMessage } + */ } return nil } @@ -383,9 +385,11 @@ class SwiftPGP: Encryption { func importKey(_ passcode: String, key: String) -> [String] { if let keyData = try? Armor.readArmored(key) { + /* if let plaintext = try? ObjectivePGP.symmetricDecrypt(keyData, key: passcode, verifyWith: nil, signed: nil, valid: nil, integrityProtected: nil), let ids = try? importKeys(data: plaintext, pw: nil, secret: true) { return ids } + */ } return [] } @@ -817,6 +821,7 @@ class SwiftPGP: Encryption { for text in textToEncrypt{ if let data = text.data(using: .utf8){ + /* if let chipher = try? ObjectivePGP.symmetricEncrypt(data, signWith: nil, encryptionKey: password, passphrase: pw, armored: false){ if armored{ chiphers.append(Armor.armored(chipher, as: PGPArmorType.message)) @@ -825,6 +830,7 @@ class SwiftPGP: Encryption { chiphers.append(chipher.base64EncodedString(options: .init(arrayLiteral: .lineLength76Characters, .endLineWithLineFeed))) } } + */ } } return (chiphers, pw) @@ -835,11 +841,13 @@ class SwiftPGP: Encryption { for chipher in chipherTexts{ if let data = chipher.data(using: .utf8){ + /* if let plainData = try? ObjectivePGP.symmetricDecrypt(data, key: password, verifyWith: nil, signed: nil, valid: nil, integrityProtected: nil){ if let plainText = String(data: plainData, encoding: .utf8){ plaintexts.append(plainText) } } + */ } } return plaintexts diff --git a/enzevalos_iphone/PermissionModel.swift b/enzevalos_iphone/PermissionModel.swift index 2392b679d4b7c1a39a03806b00b6ae3b1f963d7a..4ded1bf5ac52a65bc1b89f51089fbefa2226eb8a 100644 --- a/enzevalos_iphone/PermissionModel.swift +++ b/enzevalos_iphone/PermissionModel.swift @@ -9,15 +9,12 @@ import Contacts enum PermissionState { - case CONTACTPERMISSION, BACKGROUNDPERMISSION, FINISH + case CONTACTPERMISSION, NOTIFICATIONPERMISSION, CONTACTDENIED } class PermissionModel: ObservableObject { - @Published var currentState: PermissionState = .CONTACTPERMISSION - private var contactStore = CNContactStore() - init() { currentState = .CONTACTPERMISSION @@ -27,41 +24,42 @@ class PermissionModel: ObservableObject { func requestForAccess(_ completionHandler: @escaping (_ accessGranted: Bool) -> Void) { //requesting for authorization let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts) - + switch authorizationStatus { case .authorized: completionHandler(true) - + case .notDetermined: - self.contactStore.requestAccess(for: CNEntityType.contacts, completionHandler: { (access, accessError) -> Void in + self.contactStore.requestAccess(for: CNEntityType.contacts) { (access, accessError) -> Void in if access { completionHandler(access) } else { - if authorizationStatus == CNAuthorizationStatus.denied { - } + if authorizationStatus == CNAuthorizationStatus.denied { } completionHandler(false) } - }) - + } + default: completionHandler(false) } } - + func contactCheck(_ accessGranted: Bool) { if accessGranted { requestForNotifications() } else { - DispatchQueue.main.async(execute: { - self.showMessage(NSLocalizedString("AccessNotGranted", comment: ""), completion: self.requestForNotifications) - }); + DispatchQueue.main.async { self.currentState = .CONTACTDENIED } + do { sleep(3) } + self.requestForNotifications() } } func requestForNotifications(){ + DispatchQueue.main.async { + self.currentState = .NOTIFICATIONPERMISSION + } UNUserNotificationCenter.current().requestAuthorization(options: [ .badge], completionHandler: {didAllow, error in DispatchQueue.main.async { - self.currentState = .FINISH LetterboxModel.instance.afterPermissions() } }) @@ -69,14 +67,11 @@ class PermissionModel: ObservableObject { func showMessage(_ message: String, completion: (() -> Void)?) { let alertController = UIAlertController(title: "Letterbox", message: message, preferredStyle: UIAlertController.Style.alert) - + let dismissAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default) { (action: UIAlertAction) -> Void in - if let cb = completion { - cb() - } + if let cb = completion { cb() } } alertController.addAction(dismissAction) } - - } + diff --git a/enzevalos_iphone/Record.swift b/enzevalos_iphone/Record.swift deleted file mode 100644 index cf01697fea26152b34b7c571326f55ce558efec1..0000000000000000000000000000000000000000 --- a/enzevalos_iphone/Record.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// Record.swift -// enzevalos_iphone -// -// Created by Oliver Wiese on 06/01/17. -// Copyright © 2018 fu-berlin. -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -// - -import Foundation -import Contacts -import UIKit - - -public protocol Record: Comparable { - - var name: String { get } - var hasKey: Bool { get } - var isVerified: Bool { get } - var ezContact: AddressRecord { get } - var mails: [MailRecord] { get } - var cnContact: CNContact? { get } - var color: UIColor { get } - var image: UIImage { get } - var addresses: [MailAddress] { get } -} diff --git a/enzevalos_iphone/SMIME.swift b/enzevalos_iphone/SMIME.swift index 0a64db2315009c5ba65f9a5baad1ff3fd442bc5b..c085701adaff27877cfae1a8964474ad1fe3b40b 100644 --- a/enzevalos_iphone/SMIME.swift +++ b/enzevalos_iphone/SMIME.swift @@ -184,7 +184,7 @@ class SMIME { var outputData: Data = data let text = String(data: data, encoding: .utf8)! var fp = getOwnKeyFP() - let cert = certsKeychain[fp!]! + let cert = certsKeychain[fp!]! // TODO ERROR!!! let key = privateKeyKeychain[fp!]! var (decStr, errArr) = decryptWithPem(message: text, certAsPem: cert, keyAsPem: key) diff --git a/enzevalos_iphone/SwiftUI/Contact/ContactTabView 2.swift b/enzevalos_iphone/SwiftUI/Contact/ContactTabView 2.swift new file mode 100644 index 0000000000000000000000000000000000000000..5e2f7c6de7a1d47d73b8611c3011725a3b3538e2 --- /dev/null +++ b/enzevalos_iphone/SwiftUI/Contact/ContactTabView 2.swift @@ -0,0 +1,124 @@ +// +// ContactView.swift +// enzevalos_iphone +// +// Created by Oliver Wiese on 30.10.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// + +import SwiftUI + +struct ContactTabView <C: DisplayContact, M: DisplayMail>: View { + + public var contact: C + public var fromMail: M? + public var derivedFromKey: Bool = true + + private var keyID: String? { + return fromMail?.signatureKey?.keyID + } + + var body: some View { + VStack{ + VStack(alignment: .leading, spacing: 3) { + Text(contact.name) + .font(.title) + rating + if contact.name != contact.email { + row(label: "ContactView.EMail.Name", data: contact.email, action: composeMail) + .padding(.bottom, 10) + } + key + }.padding(5) + icon + backgroundInfo + Spacer() + } + } + + private var icon: some View { + contact.avatar + .resizable() + .frame(width: 130, height: 110) + .clipShape(Circle()) + .overlay(Circle().stroke(Color.white, lineWidth: 4)) + .shadow(radius: 10) + .padding(10) + } + + private var key: AnyView? { + if let keyID = keyID { + var label: LocalizedStringKey = "ContactView.Key.Signature.Name" + if fromMail == nil { + label = "ContactView.Key.Primary.Name" + } + return AnyView(row(label: label, data: keyID, action: inspectKey)) + } else { + return AnyView(Button("Invitation.Welcome.Try.InvitationMail", action: invite)) + } + } + + private var rating: some View { + HStack { + Text("ContactView.Rating.Name") + .foregroundColor(.gray) + Text(contact.rating(mail: fromMail).name) + .foregroundColor(contact.rating(mail: fromMail).color) + } + } + + var backgroundInfo: some View { + VStack (alignment: .leading, spacing: 3){ + if derivedFromKey, let discovery = contact.primaryKey?.discovery { + row(label: "ContactView.KnownSince", data: discovery.dateToString, action: nil) // TODO Add discovery mail? + } else if let discovery = contact.discovery { + row(label: "ContactView.KnownSince", data: discovery.dateToString, action: nil) + } else { + row(label: "ContactView.KnownSince.Unknown", data:nil, action: nil) + } + if derivedFromKey, let last = contact.primaryKey?.lastSeen { + row(label: "ContactView.LastMail.Name", data: last.dateToString, action: nil) + } + else if let last = contact.last { + row(label: "ContactView.LastMail.Name", data: last.dateToString, action: nil) + } else { + row(label: "ContactView.LastMail.No", data: nil, action: nil) + } + } .padding(10) + } + + private func row(label: LocalizedStringKey, data: String?, action: ((()->Void)?)) -> some View { + HStack { + Text(label) + .foregroundColor(.gray) + if let data = data, let action = action { + Button(data, action: action) + } else if let data = data{ + Text(data) + .foregroundColor(.gray) + } + } + } + + private func composeMail() { + print("Compose mail!") + } + + private func inspectKey() { + print("Inspect key!") + } + + private func invite() { + print("Invite user!") + } + +} + +struct ContactTabView_Previews: PreviewProvider { + static var previews: some View { + ContactTabView<ProxyContact, ProxyMail>(contact: ProxyData.Alice) + ContactTabView<ProxyContact, ProxyMail>(contact: ProxyData.Bob) + ContactTabView<ProxyContact, ProxyMail>(contact: ProxyData.Charlie) + + } +} diff --git a/enzevalos_iphone/SwiftUI/Contact/ContactTabView.swift b/enzevalos_iphone/SwiftUI/Contact/ContactTabView.swift index 5e2f7c6de7a1d47d73b8611c3011725a3b3538e2..4c6fd44cc1f90e04daf24b1461793fe8a7a61af8 100644 --- a/enzevalos_iphone/SwiftUI/Contact/ContactTabView.swift +++ b/enzevalos_iphone/SwiftUI/Contact/ContactTabView.swift @@ -8,6 +8,7 @@ import SwiftUI +/// A subview for ContactView struct ContactTabView <C: DisplayContact, M: DisplayMail>: View { public var contact: C diff --git a/enzevalos_iphone/SwiftUI/Contact/ContactView 2.swift b/enzevalos_iphone/SwiftUI/Contact/ContactView 2.swift new file mode 100644 index 0000000000000000000000000000000000000000..aba0935a46e7feef5a54f9c86071afe5dfcb191c --- /dev/null +++ b/enzevalos_iphone/SwiftUI/Contact/ContactView 2.swift @@ -0,0 +1,50 @@ +// +// ContactView.swift +// enzevalos_iphone +// +// Created by Oliver Wiese on 30.10.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// + +import SwiftUI + +// TODO: Add contact book, related addresses, related keys, related mails. + +struct ContactView <C: DisplayContact, M: DisplayMail>: View { + public var contact: C + public var fromMail: M? + public var derivedFromKey: Bool = true + + var body: some View { + TabView{ + ForEach(Tabs, id: \.id ){ tab in + tab.content + .tabItem { + tab.image + Text(tab.description) + } + } + } + .navigationTitle("ContactView.Title") + } + + var Tabs:[Tab] { + get { + return [ + Tab( + tab: 0, image: Image(systemName: "touchid"), + description: "ContactView.Tab.Contact.Description", + content:AnyView(ContactTabView(contact: contact, fromMail: fromMail, derivedFromKey: derivedFromKey)) + ) + ] + } + } +} + +struct ContactView_Previews: PreviewProvider { + static var previews: some View { + ContactView<ProxyContact, ProxyMail>(contact: ProxyData.Alice) + ContactView<ProxyContact, ProxyMail>(contact: ProxyData.Bob) + ContactView<ProxyContact, ProxyMail>(contact: ProxyData.Charlie) + } +} diff --git a/enzevalos_iphone/SwiftUI/Contact/ContactView.swift b/enzevalos_iphone/SwiftUI/Contact/ContactView.swift index aba0935a46e7feef5a54f9c86071afe5dfcb191c..9aa96309808f79e4bab044a93d4a4d7cb4d51988 100644 --- a/enzevalos_iphone/SwiftUI/Contact/ContactView.swift +++ b/enzevalos_iphone/SwiftUI/Contact/ContactView.swift @@ -15,6 +15,10 @@ struct ContactView <C: DisplayContact, M: DisplayMail>: View { public var fromMail: M? public var derivedFromKey: Bool = true +// init(contact: C, fromMail: M? = nil) { +// self.contact = contact +// } + var body: some View { TabView{ ForEach(Tabs, id: \.id ){ tab in diff --git a/enzevalos_iphone/SwiftUI/DisplayProtocols.swift b/enzevalos_iphone/SwiftUI/DisplayProtocols.swift index 74e7b7a090471cdb68d21e620aebb5140d998bc0..65b5181471b5854798b82dd4749c6ff078ccf6f1 100644 --- a/enzevalos_iphone/SwiftUI/DisplayProtocols.swift +++ b/enzevalos_iphone/SwiftUI/DisplayProtocols.swift @@ -141,6 +141,8 @@ protocol DisplayContact { } extension DisplayContact { + + func findAddress(temp: Bool) -> AddressRecord { if let addr = PersistentDataProvider.dataProvider.fetchedAddressResultController.fetchedObjects?.first { return addr diff --git a/enzevalos_iphone/SwiftUI/Inbox/MailListView 2.swift b/enzevalos_iphone/SwiftUI/Inbox/MailListView 2.swift new file mode 100644 index 0000000000000000000000000000000000000000..159ae41bc56b1595aeafbb8544500b4bd8dab6fd --- /dev/null +++ b/enzevalos_iphone/SwiftUI/Inbox/MailListView 2.swift @@ -0,0 +1,146 @@ +// +// MailListView.swift +// enzevalos_iphone +// +// Created by Oliver Wiese on 16.10.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// + +import SwiftUI +import CoreData +// TODO: Refactor to Model! +// Updating text -> Last update, updating, no connection.... + +struct MailListView: View { + @Environment(\.managedObjectContext) var managedObjectContext + var fetchRequest: FetchRequest<MailRecord> + var mails: FetchedResults<MailRecord>{fetchRequest.wrappedValue} + + var folderpath: String + var name: String + + @State var updating = false + @State var showUser = false + @State var searchText = "" + @State var searchField = 0 + @State var searchNow = false + @State var composeMail = false + + init(folderpath: String, name: String) { + fetchRequest = MailRecord.mailsInFolderFetchRequest(folderpath: folderpath) + self.folderpath = folderpath + self.name = name + } + var body: some View { + mainView + .navigationBarTitle(name, displayMode: .inline) + + .onAppear(perform: { + self.updateMails() + }) + .sheet(isPresented: $composeMail) { + ComposeView() + } + } + + private var mainView: some View { + VStack (alignment: .leading){ + SearchView(searchText: $searchText, searchField: $searchField, searchNow: $searchNow) + .padding(6) + mailList + .padding(-10) + // Toolbar + HStack { + self.idButton + Spacer() + self.lastUpdate + Spacer() + self.composeButton + } + .padding(6) + } + } + + private var mailList: some View { + List (self.mails.filter(filterKeyRecord), id: \.self){ record in + NavigationLink( + destination: ReadMainView(model: ReadModel(mail: record))) { + MailRow(mail: record) + } + } + .resignKeyboardOnDragGesture() // hide keyboard when dragging + } + + private var idButton: some View { + Button(action: { + print("Go to my id") + }, label: { + Text(NSLocalizedString("KeyID", comment: "id")) + }) + } + + private var composeButton: some View { + Button(action: { + composeMail = true + }, label: { + Image(systemName: "square.and.pencil").imageScale(.large) + }) + } + + private var lastUpdate: some View { + var text = NSLocalizedString("Updating", comment: "updating...") + if !updating { + let last = Date() + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale.current + dateFormatter.timeStyle = .medium + let dateString = dateFormatter.string(from: last) + text = NSLocalizedString("LastUpdate", comment: "") + " " + dateString + } + return Button(action: updateMails, label: {Text(text) + .font(.callout) + + }) + } + + func updateMails() { + guard !updating else { + return + } + LetterboxModel.instance.mailHandler.updateFolder(folderpath: folderpath, completionCallback: {error in + if error == nil { + self.updating = false + } + // TODO: Add error message + }) + updating = true + } + + func filterKeyRecord(keyRecord: MailRecord) -> Bool { + let searchType = SearchType.findType(i: searchField) + if self.searchText.isEmpty || self.searchText == NSLocalizedString("Searchbar.Title", comment: "Search") { + return true + } + let query = self.searchText.lowercased() + if (searchType == .All || searchType == .Sender) && (containsSearchTerms(content: keyRecord.sender.displayname, searchText: query) || containsSearchTerms(content: keyRecord.sender.email, searchText: query)) { + return true + } + else if (searchType == .All || searchType == .Sender) && keyRecord.addresses.filter({containsSearchTerms(content: $0.mailAddress, searchText: query)}).count > 0 { + return true + } + else if (searchType == .All || searchType == .Subject) && containsSearchTerms(content: keyRecord.subject, searchText: query){ + return true + } + else if (searchType == .All || searchType == .Body) && containsSearchTerms(content: keyRecord.body, searchText: query){ + return true + } + return false + } +} + +struct MailListView_Previews: PreviewProvider { + static var previews: some View { + MailListView(folderpath: "INBOX", name: "INBOX") + .environment(\.managedObjectContext, PersistentDataProvider.proxyPersistentDataProvider.persistentContainer.viewContext) + } +} diff --git a/enzevalos_iphone/SwiftUI/Inbox/MailListView.swift b/enzevalos_iphone/SwiftUI/Inbox/MailListView.swift index 5d8eb173cb78dbb72c3e73806866db6dab77244e..c88f1a300a349ffa3bbebd9ecb8b1ad9b8d63cf5 100644 --- a/enzevalos_iphone/SwiftUI/Inbox/MailListView.swift +++ b/enzevalos_iphone/SwiftUI/Inbox/MailListView.swift @@ -51,7 +51,6 @@ struct MailListView: View { .padding(-10) // Toolbar HStack { - self.idButton Spacer() self.lastUpdate Spacer() @@ -70,15 +69,7 @@ struct MailListView: View { } .resignKeyboardOnDragGesture() // hide keyboard when dragging } - - private var idButton: some View { - Button(action: { - print("Go to my id") - }, label: { - Text(NSLocalizedString("KeyID", comment: "id")) - }) - } - + private var composeButton: some View { Button(action: { composeMail = true diff --git a/enzevalos_iphone/SwiftUI/LetterboxModel.swift b/enzevalos_iphone/SwiftUI/LetterboxModel.swift index e2a4a7f4b5eee497b4f82a64cd350ca2cd030f05..b8322b6662d502b9fc0d37fb67979a77a9d5cc94 100644 --- a/enzevalos_iphone/SwiftUI/LetterboxModel.swift +++ b/enzevalos_iphone/SwiftUI/LetterboxModel.swift @@ -107,7 +107,7 @@ class LetterboxModel: ObservableObject { resetApp() StudySettings.setupStudy() if currentState == .LAUNCHEDBEFORE { - AddressHandler.updateCNContacts() + ContactHandler.handler.findContacts(update: true) // TODO: Check Account data? do { try mailHandler.startIMAPIdleIfSupported() @@ -120,7 +120,6 @@ class LetterboxModel: ObservableObject { return } }) - } else { // TODO: ONLY FOR TESTING!!!! // OutgoingMail.LoadSMIMETEST() diff --git a/enzevalos_iphone/SwiftUI/Onboarding/PermissionRequestView 2.swift b/enzevalos_iphone/SwiftUI/Onboarding/PermissionRequestView 2.swift new file mode 100644 index 0000000000000000000000000000000000000000..e1db4f287d7398a286d2291e25716a4b43bccaab --- /dev/null +++ b/enzevalos_iphone/SwiftUI/Onboarding/PermissionRequestView 2.swift @@ -0,0 +1,35 @@ +// +// PermissionRequestView.swift +// enzevalos_iphone +// +// Created by Oliver Wiese on 16.10.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// + +import SwiftUI + +struct PermissionRequestView: View { + // TODO: Text should fit to permission request. + @ObservedObject private var model = PermissionModel() + + var title: LocalizedStringKey = "Permission.AccessContacts.Title" + var description: LocalizedStringKey = "Permission.AccessContacts.Description" + + var body: some View { + VStack(alignment: .center){ + Text(title) + .font(.largeTitle) + .padding(.all) + Text(description) + .font(.body) + .padding(.all) + Spacer() + } + } +} + +struct PermissionRequestView_Previews: PreviewProvider { + static var previews: some View { + PermissionRequestView() + } +} diff --git a/enzevalos_iphone/SwiftUI/Onboarding/PermissionRequestView.swift b/enzevalos_iphone/SwiftUI/Onboarding/PermissionRequestView.swift index e1db4f287d7398a286d2291e25716a4b43bccaab..c11980bd52afc67fdd61c4b8fbd22964368900f5 100644 --- a/enzevalos_iphone/SwiftUI/Onboarding/PermissionRequestView.swift +++ b/enzevalos_iphone/SwiftUI/Onboarding/PermissionRequestView.swift @@ -9,27 +9,56 @@ import SwiftUI struct PermissionRequestView: View { - // TODO: Text should fit to permission request. @ObservedObject private var model = PermissionModel() - var title: LocalizedStringKey = "Permission.AccessContacts.Title" - var description: LocalizedStringKey = "Permission.AccessContacts.Description" + // contact access request background text + var contactPermissionTitle: LocalizedStringKey = "Permission.AccessContacts.Title" + var contactPermissionDescription: LocalizedStringKey = "Permission.AccessContacts.Description" + // notification access request background text + var notificationPermissionTitle: LocalizedStringKey = "Permission.Notification.Title" + var notificationPermissionDescription: LocalizedStringKey = "Permission.Notification.Description" + + // contact access denied background text + var contactPermissionDeniedTitle: LocalizedStringKey = "Permission.contactDenied.Title" + var contactPermissionDeniedDescription: LocalizedStringKey = "Permission.contactDenied.Description" + + // icons + var contactIcon = Image(systemName: "person.crop.circle.fill") + var notificationIcon = Image(systemName: "bell.fill") + var contactDeniedIcon = Image(systemName: "person.fill.xmark") + var body: some View { - VStack(alignment: .center){ - Text(title) - .font(.largeTitle) - .padding(.all) - Text(description) - .font(.body) - .padding(.all) + // view progression based on state of permission model + switch model.currentState { + + case .CONTACTPERMISSION : VStack{ + Text(contactIcon).font(.largeTitle) + Text(contactPermissionTitle).font(.largeTitle).padding(.all) + Text(contactPermissionDescription).font(.body).multilineTextAlignment(.center).padding(.all) Spacer() } + + case .NOTIFICATIONPERMISSION : VStack{ + Text(notificationIcon).font(.largeTitle) + Text(notificationPermissionTitle).font(.largeTitle).padding(.all) + Text(notificationPermissionDescription).font(.body).multilineTextAlignment(.center).padding(.all) + Spacer() + } + + case .CONTACTDENIED : VStack{ + Text(contactDeniedIcon).font(.largeTitle) + Text(contactPermissionDeniedTitle).font(.largeTitle).padding(.all) + Text(contactPermissionDeniedDescription).font(.body).multilineTextAlignment(.center).padding(.all) + Spacer() + } + } } -} + struct PermissionRequestView_Previews: PreviewProvider { static var previews: some View { PermissionRequestView() } } +} diff --git a/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewChildren/SmallContactListView 2.swift b/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewChildren/SmallContactListView 2.swift new file mode 100644 index 0000000000000000000000000000000000000000..b52e468ac095650a3ef73d6a848135d66963a130 --- /dev/null +++ b/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewChildren/SmallContactListView 2.swift @@ -0,0 +1,80 @@ +// +// SmallContactListView.swift +// enzevalos_iphone +// +// Created by Oliver Wiese on 06.04.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// + +import SwiftUI + +struct SmallContactListView <C: DisplayContact>: View { + let contacts: [C] + var title: String + @State var showList = true + + + var body: some View { + VStack (alignment: .leading, spacing: 10){ + HStack { + Text(title) + .font(.headline) + Button (action: { + self.showList.toggle() + }) { + + Image(systemName: showList ? "chevron.up" : "chevron.down") + .resizable() + .frame(width: 12.0, height: 7.0) + } + Spacer() + } + if showList { + ForEach(contacts, id: \.email) {contact in + Group { + HStack { + CircleImage(image: contact.avatar, radius: 40) + VStack (alignment: .leading, spacing: 2){ + Text(contact.name) + .font(.subheadline) + Text(contact.email) + .foregroundColor(.gray) + } + Spacer() + Button(action: {self.goToContact(contact: contact)}){ + Image(systemName: "chevron.right") + } + } + } + .onTapGesture { + self.goToContact(contact: contact) + } + } + } + } + .padding(10) + } + + private func goToContact(contact: C) { + /* TODOguard let con = contact.keyRecord else { + return + } */ + return + // AppDelegate.getAppDelegate().readViewCoordinator?.pushContactView(contact: con) + } +} + + +struct SmallContactListView_Previews: PreviewProvider { + static let alice = ProxyData.Alice + static let bob = ProxyData.Bob + static let charlie = ProxyData.Charlie + + static var previews: some View { + VStack{ + SmallContactListView(contacts: [alice,bob,charlie], title: "To") + + SmallContactListView(contacts: [alice,bob,charlie], title: "To", showList: false) + } + } +} diff --git a/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewChildren/SmallContactListView.swift b/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewChildren/SmallContactListView.swift index 9c8b20ec7d5054cb25c867c0d59866f6c6c1bb6f..f3debe73daeba037dfb30a854c3263e99f7d32e6 100644 --- a/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewChildren/SmallContactListView.swift +++ b/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewChildren/SmallContactListView.swift @@ -43,6 +43,7 @@ struct SmallContactListView <C: DisplayContact>: View { } Spacer() Image(systemName: "chevron.right") + } } } } diff --git a/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewMain 2.swift b/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewMain 2.swift new file mode 100644 index 0000000000000000000000000000000000000000..a5f2c9a1c1f26a455ad01e2dc76d7c1a6616014f --- /dev/null +++ b/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewMain 2.swift @@ -0,0 +1,177 @@ +// +// SenderViewMain.swift +// enzevalos_iphone +// +// Created by fatimaaa96 on 12.03.20. +// Copyright © 2020 fu-berlin. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +import SwiftUI +import CoreLocation + +struct SenderViewMain <M: DisplayMail>: View { + + // SEE: PersonNameComponents + + @EnvironmentObject var model: ReadModel<M> + + @State var selectedLandmark: Landmark? = nil + @State var showingLandmarkDetails = false + + var body: some View { + ScrollView { + sender + .padding(.top, 110/2-20) + .padding() + .offset(y: (-110/2 + 20)) + icon + SmallContactListView(contacts: model.mail.tos, title: NSLocalizedString("To", comment: "To")) + Divider() + SmallContactListView(contacts: model.mail.ccs, title: NSLocalizedString("Cc", comment: "CC")) + Divider() + SmallContactListView(contacts: model.mail.bccs, title: NSLocalizedString("Bcc", comment: "BCC")) + } + } + + let Landmarks = [ + Landmark(name: "Berlin", domain: "exampledomain.de", location: .init(latitude: 52.520008, longitude: 13.404954)), + Landmark(name: "New York", domain: "secondexampledomain.de", location: .init(latitude: 40.730610, longitude: -73.935242)), + Landmark(name: "Sydney", domain: "thirdexampledomain.de", location: .init(latitude: -33.865143, longitude: 151.209900)) + ] + + private func selectNextLandmark() { + let landmarks = Landmarks// mail.routingStops + /// This method identifies and moves to the next selected landmark + if let selectedLandmark = selectedLandmark, let currentIndex = landmarks.firstIndex(where: { $0 == selectedLandmark }), currentIndex + 1 < landmarks.endIndex { + self.selectedLandmark = landmarks[currentIndex + 1] + } else { + selectedLandmark = landmarks.first + } + } + + private func selectPrevLandmark() { + let landmarks = Landmarks //mail.routingStops + /// This method identifies and moves to the prevoius selected landmark + if let selectedLandmark = selectedLandmark, let currentIndex = landmarks.firstIndex(where: { $0 == selectedLandmark }), currentIndex + -1 >= 0 { + self.selectedLandmark = landmarks[currentIndex - 1] + } else { + selectedLandmark = landmarks.last + } + } + + private var icon: some View { + model.mail.sender.avatar + .resizable() + .frame(width: 130, height: 110) + .clipShape(Circle()) + .overlay(Circle().stroke(Color.white, lineWidth: 4)) + .shadow(radius: 10) + .offset(y: -110/2) + .padding(.bottom, -110/2) + .onTapGesture { + self.goToContact(contact: self.model.mail.sender) + } + } + + private var sender: some View { + VStack (alignment: .leading, spacing: 10) { + HStack { + VStack (alignment: .leading) { + Text(NSLocalizedString("Sender", comment: "Sender")) + .font(.headline) + .padding(.bottom, 10) + Text(model.mail.sender.name) + .font(.subheadline) + Text(model.mail.sender.email) + .foregroundColor(.gray) + } + Spacer() + Button(action: {self.goToContact(contact: self.model.mail.sender)}){ + Image(systemName: "chevron.right") + } + } + .onTapGesture { + self.goToContact(contact: self.model.mail.sender) + } + HStack{ + Text(String(format: NSLocalizedString("ReadView.Sender.Previous", comment: "100 previous received mails"), model.mail.sender.previousMails)) + Spacer() + Text(String(format: NSLocalizedString("ReadView.Sender.Responses", comment: "5 previous sent mails"), model.mail.sender.previousResponses)) + // TODO: Add last mail date + // TODO: Go to mails? + } + } + .padding(10) + } + + private var map: some View { + ZStack { + ZStack { + MapView(landmarks: Landmarks,//mail.routingStops, + selectedLandmark: $selectedLandmark, showingLandmarkDetails: $showingLandmarkDetails) + .frame(height: 300) + HStack { + Button(action: { + /// this button displays the previous selected landmark + self.selectPrevLandmark() + }) { + Text("Button.Prev") + .foregroundColor(.black) + .padding() + .background(Color.white) + .cornerRadius(6) + .shadow(radius: 3) + .padding(.top, 150) + } + + Spacer() + Button(action: { + /// this button displays the next selected landmark + self.selectNextLandmark() + }) { + Text("Button.Next") + .foregroundColor(.black) + .padding() + .background(Color.white) + .cornerRadius(6) + .shadow(radius: 3) + .padding(.top, 150) + } + } + } .alert(isPresented: $showingLandmarkDetails) { + /// alert displays the landmark details and gets trigered when the user taps the information button of a landmark + Alert(title: Text("Domain for this location"), message: Text(selectedLandmark?.domain ?? "Missing place information"), dismissButton: .default(Text("OK")) ) + } + } + } + + private func goToContact(contact: M.C) { + /* guard let con = contact.keyRecord else { + print("No record...") + return + } + AppDelegate.getAppDelegate().readViewCoordinator?.pushContactView(contact: con) */ + } +} + +struct SenderView_Previews: PreviewProvider { + static var model = ReadModel(mail: ProxyData.SecureMail) + static var previews: some View { + model.currentTab = ReadPart.Header.value + let sim = Simulators<ReadMainView<ProxyMail>>() + return sim.previews(view: ReadMainView(model: model)) + } +} diff --git a/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewMain.swift b/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewMain.swift index fe0ca99f1f999dad060ac106e94c276a70d36312..5426af55d95a15bb985fc8c0210abf361465485b 100644 --- a/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewMain.swift +++ b/enzevalos_iphone/SwiftUI/Read/Tabbed Views/SenderViewMain.swift @@ -87,24 +87,22 @@ struct SenderViewMain <M: DisplayMail>: View { private var sender: some View { VStack (alignment: .leading, spacing: 10) { - HStack { - VStack (alignment: .leading) { - Text(NSLocalizedString("Sender", comment: "Sender")) - .font(.headline) - .padding(.bottom, 10) - Text(model.mail.sender.name) - .font(.subheadline) - Text(model.mail.sender.email) - .foregroundColor(.gray) + Text(NSLocalizedString("Sender", comment: "Sender")) + .font(.headline) + .padding(.bottom, 10) + NavigationLink(destination: ContactView(contact: model.mail.sender, fromMail: model.mail)) { + HStack { + VStack (alignment: .leading) { + Text(model.mail.sender.name) + .font(.headline) + Text(model.mail.sender.email) + .foregroundColor(.secondary) + } } Spacer() - Button(action: {self.goToContact(contact: self.model.mail.sender)}){ - Image(systemName: "chevron.right") - } - } - .onTapGesture { - self.goToContact(contact: self.model.mail.sender) + Image(systemName: "chevron.right") } + HStack{ Text(String(format: NSLocalizedString("ReadView.Sender.Previous", comment: "100 previous received mails"), model.mail.sender.previousMails)) Spacer() diff --git a/enzevalos_iphone/de.lproj/Localizable.strings b/enzevalos_iphone/de.lproj/Localizable.strings index fb85676d368b7ae3847df401862b69ed5ded0bbd..9645bd31ec6059083cfd1c261c2c42d24bfe969f 100644 --- a/enzevalos_iphone/de.lproj/Localizable.strings +++ b/enzevalos_iphone/de.lproj/Localizable.strings @@ -5,8 +5,13 @@ Created by jakobsbode on 06.10.16. Copyright © 2016 fu-berlin. All rights reserved. */ +"Permission.contactDenied.Title" = "Oops! Zugriff erlauben!"; +"Permission.contactDenied.Description" = "Bitte erlaube zugriff auf Kontakte In Einstellungen -> Datenschutz -> Kontakte"; +"Permission.Notification.Title" = "Mitteilungen erlauben"; +"Permission.Notification.Description" = "Letterbox möchte dich benachrichtigen. +Du kannst das jederzeit ändern. In der Einstellungen -> Mitteilungen -> Letterbox"; "Permission.AccessContacts.Title" = "Zugriff auf Kontakte"; -"Permission.AccessContacts.Description" = "Damit diese App richtig funktioniert, brauchen wir Zugriff auf deine Kontakte. Wir teilen diese Daten mit niemandem und senden sie auch nicht über das Internet."; +"Permission.AccessContacts.Description" = "Wir teilen diese Daten mit niemandem und senden sie auch nicht über das Internet."; "AccessNotGranted" = "Bitte schalte unter Einstellungen den Zugriff auf die Kontakte frei, wenn du möchtest, dass die App richtig funktioniert"; "Archive" = "Archiv"; "Address" = "Adresse"; @@ -61,7 +66,6 @@ "Junk" = "Spam"; "KeyAddresses" = "Mailadressen aus dem Schlüssel"; "KeyDetails" = "Schlüsseldetails"; -"KeyID" = "ID"; "KeyIsRevoked" = "Der Schlüssel wurde zurückgezogen. Zurückgezogen wurde er am "; "KeyIsVerified" = "Der Schlüssel ist verifiziert. Verifiziert wurde er am "; "KeyNotFound" = "Der Schlüssel konnte nicht gefunden werden. Dies ist ein Fehler, bitte wende dich an die Entwickler!"; @@ -440,13 +444,15 @@ "ReadView.SecurityBriefing.Conf.State.UnableToDecrypt" = "Dies ist eine vertrauliche Mail, aber sie kann nicht entschlüsselt werden. Wenn du die Mail auf einem anderen Gerät lesen kannst, solltest du die Geräte abgleichen. Ansonsten hat der Absender vielleicht einen Fehler gemacht und du solltest ihn bitten die Mail nochmal zu schicken."; "ReadView.SecurityBriefing.Conf.Problem.PrivacyPolicy" = "At least one involved email provider read the mail advertising as they say in their privicy policy."; "ReadView.SecurityBriefing.Auth.Title" = "Wer ist der Absender?"; -"ReadView.SecurityBriefing.Auth.State.UnSigned" = "Wir wissen nicht, ob der tatsächliche Absender der Absenderaddresse entspricht. Bitte beachte, dass es sich womöglich um einen Betrüger handeln könnte."; -"ReadView.SecurityBriefing.Auth.State.Signed" = "Der Absender hat eine ID und wir haben eine glaubwürdige vorherige Mail."; +"ReadView.SecurityBriefing.Auth.State.UnSigned" = "Wir wissen nicht, ob der Absenderaddresse der tatsächliche Absender ist. Sei wachsam! Es könnte ein womöglich auch ein Betrüger sein."; +"ReadView.SecurityBriefing.Auth.State.Signed" = "Der Absender hat eine ID und wir haben eine glaubwürdige vorherige Mails."; "ReadView.SecurityBriefing.Auth.State.MissingPK" = "Der Absender kann nicht verifiziert werden, weil die Absender ID fehlt. Aber falls diese importiert wird, wird dies nachgeholt."; "ReadView.SecurityBriefing.Auth.State.InvalidSignature" = "Diese Mail wurde verändert! Bitte sei misstrauisch, falls du trotzdem die Mail liest."; -"ReadView.SecurityBriefing.EncryptionType.Text" = "Diese Email wurde verschlüsselt mit "; "ReadView.SecurityBriefing.Actions.Title" = "Erhöhe die Sicherheit"; -"ReadView.SecurityBriefing.Actions.Description" = "Wir können die Sicherheit dieser und weiterer Mails in Zukunft verbessern."; +"ReadView.SecurityBriefing.Actions.Description" = "Wir können die Sicherheit dieser und weitere Mails in Zukunft verbessern."; "ReadView.PrefilledMail.AskForPK.Body" = "Hey, \nIch konnte die letzte Mail nicht verifizieren, weil ich deinen öffentlichen Schlüssel nicht habe. Kannst du mir deinen öffentlichen Schlüssel schicken? Vielen Dank und viele Grüße"; "ReadView.PrefilledMail.AskForPK.Subject" = "Fehlender öffentlicher Schlüssel"; "ReadView.PrefilledMail.AskForResend.Body" = "Hey, \nIch konnte deine letzte Mail nicht entschlüsseln, weil ich den geheimen Schlüssel nicht habe. Kannst du die Mail nochmal mit dem angehängten öffentlichen Schlüssel verschlüsseln? Vielen Dank und viele Grüße"; + + + diff --git a/enzevalos_iphone/en.lproj/Localizable.strings b/enzevalos_iphone/en.lproj/Localizable.strings index a94dfd6d71ee62aa54b149a699853d3d55f9a1bf..bad3e4d9db6f943d5ccbde29fbff0fba8e90e209 100644 --- a/enzevalos_iphone/en.lproj/Localizable.strings +++ b/enzevalos_iphone/en.lproj/Localizable.strings @@ -5,8 +5,12 @@ Created by jakobsbode on 06.10.16. Copyright © 2016 fu-berlin. All rights reserved. */ -"Permission.AccessContacts.Title" = "Access contacts"; -"Permission.AccessContacts.Description" = "This App requires access to your contacts to work properly. We do not share your data with others or transfer it over the internet."; +"Permission.contactDenied.Title" = "Oops! Please Allow!"; +"Permission.contactDenied.Description" = "Please Turn On Access Contacts In Settings -> Privacy -> Contacts ."; +"Permission.Notification.Title" = "Allow Notifications"; +"Permission.Notification.Description" = "Letterbox wants to notify you. You can change it anytime in Settings -> Notifications -> Letterbox"; +"Permission.AccessContacts.Title" = "Access Contacts"; +"Permission.AccessContacts.Description" = "Letterbox needs to access your contacts to work properly. Your data would be safe and not accessible by third parties or shared on the internet."; "AccessNotGranted" = "Please allow access to contacts in settings, if you want the app to work properly."; // TODO "Archive" = "Archive"; "Address" = "Address"; @@ -61,7 +65,6 @@ "Junk" = "Junk"; "KeyAddresses" = "Mail addresses from the key"; "KeyDetails" = "Key Details"; -"KeyID" = "ID"; "KeyIsRevoked" = "The Key was revoked. It was revoked on "; "KeyIsVerified" = "The Key is verified. It was verified on "; "KeyNotFound" = "No Key Found. This is an error, contact the developers!"; @@ -419,13 +422,14 @@ "ReadView.SecurityBriefing.Conf.State.UnableToDecrypt" = "This is a confidential mail, but it could not be decrypted. If you can read this mail on other devices, you should synchronize them. If not, the sender might have made a mistake and you can ask the sender to send the mail again."; "ReadView.SecurityBriefing.Conf.Problem.PrivacyPolicy" = "At least one involved email provider read the mail advertising as they say in their privicy policy."; "ReadView.SecurityBriefing.Auth.Title" = "Who is the sender?"; -"ReadView.SecurityBriefing.Auth.State.UnSigned" = "We don't know if the sender address represents the real sender. Be aware that a fraud can impersonate the sender."; +"ReadView.SecurityBriefing.Auth.State.UnSigned" = "we do not know if the sender address represents the real sender. Be aware that a fraud can impersonate the sender."; "ReadView.SecurityBriefing.Auth.State.Signed" = "The senders of these mails have an ID and we have a genuine record about previous mails of them."; "ReadView.SecurityBriefing.Auth.State.MissingPK" = "We can not verify the sender because sender's ID is missing. But after importing sender's ID we can verify the sender."; "ReadView.SecurityBriefing.Auth.State.InvalidSignature" = "This mail has been manipulated! If you want to read it anyway, please be cautions."; -"ReadView.SecurityBriefing.EncryptionType.Text" = "This email was encrypted using "; "ReadView.SecurityBriefing.Actions.Title" = "Improving the security"; -"ReadView.SecurityBriefing.Actions.Description" = "We can improve the security of this email and other emails in the future."; +"ReadView.SecurityBriefing.Actions.Description" = "We can improve the security of this email and other emails in the future"; "ReadView.PrefilledMail.AskForPK.Body" = "Hey, \nI could not verify your last email because I do not have your public key. Can you send me your public key? Thank you and best regards"; "ReadView.PrefilledMail.AskForPK.Subject" = "Missing your public key"; "ReadView.PrefilledMail.AskForResend.Body" = "Hey, \nI could not decrypt your last email because I do not have the secret key. Can you resend the email again and encrypt the email for the attachted secret key? Thank you and best regards"; + + diff --git a/enzevalos_iphone/mail/IncomingMail.swift b/enzevalos_iphone/mail/IncomingMail.swift index 1235050fb5033834916eac77c60d0cc23d20e264..93e71adf431535a93c2a641cd65428e55276c7ed 100644 --- a/enzevalos_iphone/mail/IncomingMail.swift +++ b/enzevalos_iphone/mail/IncomingMail.swift @@ -274,6 +274,32 @@ class IncomingMail { } let encState = self.cryptoObj?.encryptionState.rawValue ?? 0 + var lastsignedDate: Date? = nil + if let sigState = self.cryptoObj?.signatureState, sigState == .ValidSignature{ + lastsignedDate = self.date + } + var usedAddr = [AddressProperties]() + if let from = from { + usedAddr.append(from) + } + + var autocryptPK = [PublicKeyProperties]() + for fpr in newAutocrypPublicKeys { + var signed: Date? = nil + if let sigendKeys = self.cryptoObj?.signedKeys, sigendKeys.contains(fpr) { + signed = lastsignedDate + } + let pk = PublicKeyProperties(fingerprint: fpr, cryptoProtocol: .PGP, origin: .AutocryptHeader, preferEncryption: autocrypt?.prefer_encryption, lastSeenInAutocryptHeader: date, lastSeenSignedMail: signed, secretKeyProperty: nil, usedAddresses: usedAddr) + autocryptPK.append(pk) + } + + var attPK = [PublicKeyProperties]() + for fpr in newPublicKeys { + let pk = PublicKeyProperties(fingerprint: fpr, cryptoProtocol: .PGP, origin: .MailAttachment, preferEncryption: nil, lastSeenInAutocryptHeader: nil, lastSeenSignedMail: nil, secretKeyProperty: nil, usedAddresses: usedAddr) + attPK.append(pk) + } + + var attachments = [AttachmentProperties]() for att in readableAttachments{ // TODO add missing fields @@ -281,15 +307,13 @@ class IncomingMail { attachments.append(property) } let f = Int16(self.flags.rawValue) - var m = MailProperties(messageID: self.msgID, subject: subject, date: date, flags: f, from: from, to: to, cc: ccProperties, bcc: bccProperties, folder: folder, body: body, signatureState: sigState, encryptionState: encState, signatureKey: sigKey, decryptionKey: nil, autocryptHeaderKey: nil, attachedPublicKeys: [], attachedSecretKeys: []) + + var m = MailProperties(messageID: self.msgID, subject: subject, date: date, flags: f, from: from, to: to, cc: ccProperties, bcc: bccProperties, folder: folder, body: body, signatureState: sigState, encryptionState: encState, signatureKey: sigKey, decryptionKey: nil, autocryptHeaderKey: autocryptPK, attachedPublicKeys: attPK, attachedSecretKeys: []) // TODO: FIX KEYS m.to = to m.cc = ccProperties m.bcc = bccProperties m.attachments = attachments - if attachments.count > 0 { - print("\(m.subject) # attachments: \(m.attachments.count)") - } return m } diff --git a/enzevalos_iphone/persistentData/AddressRecord+CoreDataProperties.swift b/enzevalos_iphone/persistentData/AddressRecord+CoreDataProperties.swift index 62141ae4b4e242921acf1767fb36a2534f1a334c..8438b8c8e70f3e0689066890b26a6fcfd1074932 100644 --- a/enzevalos_iphone/persistentData/AddressRecord+CoreDataProperties.swift +++ b/enzevalos_iphone/persistentData/AddressRecord+CoreDataProperties.swift @@ -17,6 +17,7 @@ extension AddressRecord { } @NSManaged public var displayname: String? + @NSManaged public var image: Data? @NSManaged public var email: String @NSManaged public var phoneBookID: String? @NSManaged public var inBCCField: NSSet? diff --git a/enzevalos_iphone/persistentData/AddressRecord.swift b/enzevalos_iphone/persistentData/AddressRecord.swift index e67d197b631a9d475541918c0a78ca53617010af..4148aa7dae58bf648ad5a3edbd46b65eac00d0a4 100644 --- a/enzevalos_iphone/persistentData/AddressRecord.swift +++ b/enzevalos_iphone/persistentData/AddressRecord.swift @@ -118,6 +118,13 @@ extension AddressRecord { return false } } + + public var isUser: Bool { + if let user = UserManager.loadUserValue(.userAddr) as? String { + return user == email + } + return false + } } extension AddressRecord: DisplayContact { @@ -128,7 +135,7 @@ extension AddressRecord: DisplayContact { } var isInContactBook: Bool { - return false // TODO + return self.phoneBookID != nil } var otherAddresses: [C] { @@ -201,3 +208,12 @@ extension AddressRecord: DisplayContact { } } } + +extension AddressRecord { + var mcoAddress: MCOAddress { + get { + let name = displayname ?? email + return MCOAddress.init(displayName: name, mailbox: email) + } + } +} diff --git a/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/.xccurrentversion b/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/.xccurrentversion new file mode 100644 index 0000000000000000000000000000000000000000..7f18e93486e2d38d6da8a678f1aa0240856aa10a --- /dev/null +++ b/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>_XCCurrentVersionName</key> + <string>DataModel2.xcdatamodel</string> +</dict> +</plist> diff --git a/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents b/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents index 2d59f5b8f1f6b2e426beca8378fbce27217bf728..38174659d6f247308297e82e98a9678d9c1e99fc 100644 --- a/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents +++ b/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="19H114" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> +<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="19H524" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> <entity name="AbstractKeyRecord" representedClassName="AbstractKeyRecord" isAbstract="YES" syncable="YES" codeGenerationType="class"> <attribute name="cryptoProtocol" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="fingerprint" attributeType="String"/> diff --git a/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel2.xcdatamodel/contents b/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel2.xcdatamodel/contents new file mode 100644 index 0000000000000000000000000000000000000000..092ae1602697da32fb7c48ef74c11550a1f7cddb --- /dev/null +++ b/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel2.xcdatamodel/contents @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="19H524" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> + <entity name="AbstractKeyRecord" representedClassName="AbstractKeyRecord" isAbstract="YES" syncable="YES" codeGenerationType="class"> + <attribute name="cryptoProtocol" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="fingerprint" attributeType="String"/> + <attribute name="isSecret" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <uniquenessConstraints> + <uniquenessConstraint> + <constraint value="fingerprint"/> + <constraint value="isSecret"/> + </uniquenessConstraint> + </uniquenessConstraints> + </entity> + <entity name="AddressRecord" representedClassName="AddressRecord" syncable="YES"> + <attribute name="displayname" optional="YES" attributeType="String"/> + <attribute name="email" attributeType="String"/> + <attribute name="image" optional="YES" attributeType="Binary"/> + <attribute name="phoneBookID" optional="YES" attributeType="String"/> + <relationship name="inBCCField" toMany="YES" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="bccAddresses" inverseEntity="MailRecord"/> + <relationship name="inCCField" toMany="YES" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="ccAddresses" inverseEntity="MailRecord"/> + <relationship name="inFromField" toMany="YES" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="fromAddress" inverseEntity="MailRecord"/> + <relationship name="inToField" toMany="YES" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="toAddresses" inverseEntity="MailRecord"/> + <relationship name="primaryKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PublicKeyRecord" inverseName="isPrimaryKeyOf" inverseEntity="PublicKeyRecord"/> + <relationship name="usedPublicKeys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PublicKeyRecord" inverseName="usedAddress" inverseEntity="PublicKeyRecord"/> + <relationship name="usedSecretKeys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="SecretKeyRecord" inverseName="usedAddress" inverseEntity="SecretKeyRecord"/> + <uniquenessConstraints> + <uniquenessConstraint> + <constraint value="email"/> + </uniquenessConstraint> + </uniquenessConstraints> + </entity> + <entity name="AttachmentRecord" representedClassName="AttachmentRecord" syncable="YES" codeGenerationType="class"> + <attribute name="contentID" optional="YES" attributeType="String"/> + <attribute name="data" attributeType="Binary"/> + <attribute name="mimeType" optional="YES" attributeType="String"/> + <attribute name="name" attributeType="String"/> + <attribute name="type" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <relationship name="isPartOfMail" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="includedAttachments" inverseEntity="MailRecord"/> + </entity> + <entity name="FolderRecord" representedClassName="FolderRecord" syncable="YES" codeGenerationType="class"> + <attribute name="delimiter" optional="YES" attributeType="String"/> + <attribute name="flags" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="lastUpdate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="maxUID" optional="YES" attributeType="Decimal" defaultValueString="0.0"/> + <attribute name="minUID" optional="YES" attributeType="Decimal" defaultValueString="0.0"/> + <attribute name="path" attributeType="String"/> + <attribute name="uidValidity" optional="YES" attributeType="Decimal" defaultValueString="0.0"/> + <relationship name="childrenFolder" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="FolderRecord" inverseName="partentFolder" inverseEntity="FolderRecord"/> + <relationship name="mailsInFolder" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="inFolder" inverseEntity="MailRecord"/> + <relationship name="partentFolder" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="FolderRecord" inverseName="childrenFolder" inverseEntity="FolderRecord"/> + <uniquenessConstraints> + <uniquenessConstraint> + <constraint value="path"/> + </uniquenessConstraint> + </uniquenessConstraints> + </entity> + <entity name="MailRecord" representedClassName="MailRecord" syncable="YES"> + <attribute name="body" attributeType="String"/> + <attribute name="date" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="encryptionStateInt" attributeType="Integer 16" minValueString="0" maxValueString="3" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="flag" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="messageID" attributeType="String"/> + <attribute name="signatureStateInt" attributeType="Integer 16" minValueString="-1" maxValueString="2" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="subject" attributeType="String"/> + <attribute name="uID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="xMailer" optional="YES" attributeType="String"/> + <relationship name="attachedAutocryptPublicKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PublicKeyRecord" inverseName="sendWithAutocryptHeader" inverseEntity="PublicKeyRecord"/> + <relationship name="attachedPublicKeys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PublicKeyRecord" inverseName="sendWithMail" inverseEntity="PublicKeyRecord"/> + <relationship name="attachedSecretKey" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="SecretKeyRecord" inverseName="sendWithMail" inverseEntity="SecretKeyRecord"/> + <relationship name="bccAddresses" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="AddressRecord" inverseName="inBCCField" inverseEntity="AddressRecord"/> + <relationship name="ccAddresses" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="AddressRecord" inverseName="inCCField" inverseEntity="AddressRecord"/> + <relationship name="decryptionKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SecretKeyRecord" inverseName="decryptedMails" inverseEntity="SecretKeyRecord"/> + <relationship name="fromAddress" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="AddressRecord" inverseName="inFromField" inverseEntity="AddressRecord"/> + <relationship name="includedAttachments" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="AttachmentRecord" inverseName="isPartOfMail" inverseEntity="AttachmentRecord"/> + <relationship name="inFolder" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="FolderRecord" inverseName="mailsInFolder" inverseEntity="FolderRecord"/> + <relationship name="signatureKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PublicKeyRecord" inverseName="signedMails" inverseEntity="PublicKeyRecord"/> + <relationship name="toAddresses" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="AddressRecord" inverseName="inToField" inverseEntity="AddressRecord"/> + <uniquenessConstraints> + <uniquenessConstraint> + <constraint value="messageID"/> + </uniquenessConstraint> + </uniquenessConstraints> + </entity> + <entity name="PublicKeyRecord" representedClassName="PublicKeyRecord" parentEntity="AbstractKeyRecord" syncable="YES" codeGenerationType="class"> + <attribute name="discovery" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="expire" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="lastSeenInAutocryptHeader" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="lastSeenSignedMail" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="origin" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="preferEncryption" optional="YES" attributeType="Integer 16" usesScalarValueType="YES"/> + <attribute name="receivedMyPublicKey" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="verificationDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <relationship name="isPrimaryKeyOf" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="AddressRecord" inverseName="primaryKey" inverseEntity="AddressRecord"/> + <relationship name="mySecretKeyRecord" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SecretKeyRecord" inverseName="myPublicKeyRecord" inverseEntity="SecretKeyRecord"/> + <relationship name="sendWithAutocryptHeader" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="attachedAutocryptPublicKey" inverseEntity="MailRecord"/> + <relationship name="sendWithMail" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="attachedPublicKeys" inverseEntity="MailRecord"/> + <relationship name="signedMails" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="signatureKey" inverseEntity="MailRecord"/> + <relationship name="usedAddress" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="AddressRecord" inverseName="usedPublicKeys" inverseEntity="AddressRecord"/> + </entity> + <entity name="SecretKeyRecord" representedClassName="SecretKeyRecord" parentEntity="AbstractKeyRecord" syncable="YES" codeGenerationType="class"> + <attribute name="exported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="importedDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <relationship name="decryptedMails" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="decryptionKey" inverseEntity="MailRecord"/> + <relationship name="myPublicKeyRecord" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="PublicKeyRecord" inverseName="mySecretKeyRecord" inverseEntity="PublicKeyRecord"/> + <relationship name="sendWithMail" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MailRecord" inverseName="attachedSecretKey" inverseEntity="MailRecord"/> + <relationship name="usedAddress" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="AddressRecord" inverseName="usedSecretKeys" inverseEntity="AddressRecord"/> + </entity> + <fetchRequest name="FetchAllPublicKeys" entity="PublicKeyRecord"/> + <fetchRequest name="FetchAllSecretKeys" entity="SecretKeyRecord"/> + <elements> + <element name="AbstractKeyRecord" positionX="35.984375" positionY="-344.375" width="128" height="88"/> + <element name="AddressRecord" positionX="91.82421875" positionY="34.6484375" width="128" height="208"/> + <element name="AttachmentRecord" positionX="-602.1484375" positionY="-73.89453125" width="128" height="133"/> + <element name="FolderRecord" positionX="-498.28125" positionY="219.9140625" width="128" height="193"/> + <element name="MailRecord" positionX="-291.875" positionY="-121.84375" width="128" height="343"/> + <element name="PublicKeyRecord" positionX="141.7421875" positionY="-257.57421875" width="128" height="28"/> + <element name="SecretKeyRecord" positionX="-107.44140625" positionY="-231.81640625" width="128" height="133"/> + </elements> +</model> \ No newline at end of file diff --git a/enzevalos_iphone/persistentData/PersistentDataProvider.swift b/enzevalos_iphone/persistentData/PersistentDataProvider.swift index c9a4dc0cb0c86afc670eb3f886eff8c2c1135926..7f2994f299ee793b21db39e769a4a1f1d000dbba 100644 --- a/enzevalos_iphone/persistentData/PersistentDataProvider.swift +++ b/enzevalos_iphone/persistentData/PersistentDataProvider.swift @@ -231,7 +231,7 @@ class PersistentDataProvider { self.importOneMail(m: m, taskContext: taskContext) } else if let key = d as? PublicKeyProperties { - self.importeOneKey(publicKey: key, taskContext: taskContext) + _ = self.importeOneKey(publicKey: key, taskContext: taskContext) } else { // Create a managed object on the private queue context. @@ -256,7 +256,7 @@ class PersistentDataProvider { } - private func importeOneKey(publicKey: PublicKeyProperties, taskContext: NSManagedObjectContext) { + private func importeOneKey(publicKey: PublicKeyProperties, taskContext: NSManagedObjectContext) -> PublicKeyRecord { let publicKeyRecord: PublicKeyRecord if let pk = taskContext.findPublicKeys(fingerprint: publicKey.fingerprint)?.first { publicKeyRecord = pk @@ -291,6 +291,7 @@ class PersistentDataProvider { importManyAddresses(addrs: addresses , taskContext: taskContext, addTo: secretKeyRecord.addToUsedAddress) } } + return publicKeyRecord } private func importOneMail(m: MailProperties, taskContext: NSManagedObjectContext) { @@ -357,24 +358,27 @@ class PersistentDataProvider { } - // Handle related cryptokeys + // Handling keys if let signedKey = m.signatureKey { - let key = NSEntityDescription.insertNewObject(forEntityName: PublicKeyRecord.entityName, into: taskContext) as! PublicKeyRecord + let key = importeOneKey(publicKey: signedKey, taskContext: taskContext) key.update(property: signedKey) mail.signatureKey = key } if let decKey = m.decryptionKey { - let key = NSEntityDescription.insertNewObject(forEntityName: SecretKeyRecord.entityName, into: taskContext) as! SecretKeyRecord - key.update(property: decKey) - mail.decryptionKey = key + let publicKeyProperties = PublicKeyProperties(fingerprint: decKey.fingerprint, cryptoProtocol: decKey.cryptoProtocol, origin: nil, preferEncryption: nil, lastSeenInAutocryptHeader: nil, lastSeenSignedMail: nil, secretKeyProperty: decKey,usedAddresses: decKey.usedAddresses) + let key = importeOneKey(publicKey: publicKeyProperties, taskContext: taskContext) + if let secKey = key.mySecretKeyRecord { + secKey.update(property: decKey) + mail.decryptionKey = secKey + } } - if let autocryptKey = m.autocryptHeaderKey { - let key = NSEntityDescription.insertNewObject(forEntityName: PublicKeyRecord.entityName, into: taskContext) as! PublicKeyRecord + if let autocryptKey = m.autocryptHeaderKey.first { // TODO: Fix multiple Autocrypt keys? + let key = importeOneKey(publicKey: autocryptKey, taskContext: taskContext) key.update(property: autocryptKey) mail.attachedAutocryptPublicKey = key } for pk in m.attachedPublicKeys { - let key = NSEntityDescription.insertNewObject(forEntityName: PublicKeyRecord.entityName, into: taskContext) as! PublicKeyRecord + let key = importeOneKey(publicKey: pk, taskContext: taskContext) key.update(property: pk) mail.addToAttachedPublicKeys(key) } @@ -578,12 +582,22 @@ class PersistentDataProvider { var predicates = [NSPredicate]() if let myPredicates = equalPredicates { for (k,v) in myPredicates { - predicates.append(NSPredicate(format: "\(k) == %@", v)) // TODO REFACTOR: Predicates are created in Record files??? + if v == "nil" { + predicates.append(NSPredicate(format: "\(k) == nil")) + + } else { + predicates.append(NSPredicate(format: "\(k) == %@", v)) // TODO REFACTOR: Predicates are created in Record files??? + } } } if let myPredicates = differentPredicates { for (k,v) in myPredicates { - predicates.append(NSPredicate(format: "\(k) != %@", v)) + if v == "nil" { + predicates.append(NSPredicate(format: "\(k) != nil")) + + } else { + predicates.append(NSPredicate(format: "\(k) != %@", v)) // TODO REFACTOR: Predicates are created in Record files??? + } } } if let myPredicates = inPredicates { @@ -627,8 +641,10 @@ class PersistentDataProvider { var myContext: NSManagedObjectContext if let con = context { myContext = con + } else if Thread.isMainThread { + myContext = persistentContainer.viewContext } else { - myContext = Thread.isMainThread ? persistentContainer.viewContext : persistentContainer.newBackgroundContext() + myContext = newTaskContext() } let controller = NSFetchedResultsController(fetchRequest: freq, managedObjectContext: myContext, sectionNameKeyPath: nil, cacheName: nil) // Perform the fetch. diff --git a/enzevalos_iphone/persistentData/Properties.swift b/enzevalos_iphone/persistentData/Properties.swift index c605f84876f2b4f4b77b6b3f4dacd694938d738a..7db6b0d8671576005d7c27737a1a0467b49ae85c 100644 --- a/enzevalos_iphone/persistentData/Properties.swift +++ b/enzevalos_iphone/persistentData/Properties.swift @@ -43,7 +43,7 @@ struct MailProperties: DataPropertyProtocol { var signatureKey: PublicKeyProperties? var decryptionKey: SecretKeyProperties? - var autocryptHeaderKey: PublicKeyProperties? + var autocryptHeaderKey: [PublicKeyProperties] = [] var attachedPublicKeys: [PublicKeyProperties] = [] var attachedSecretKeys: [SecretKeyProperties] = [] @@ -103,7 +103,7 @@ struct PublicKeyProperties: DataPropertyProtocol { let fingerprint: String let cryptoProtocol: CryptoScheme - var origin: Origin + var origin: Origin? var preferEncryption: AutocryptState? var lastSeenInAutocryptHeader: Date? diff --git a/enzevalos_iphone/persistentData/PublicKeyRecord.swift b/enzevalos_iphone/persistentData/PublicKeyRecord.swift index 6f2f711bf7b2de3aa751506839ccd8efeb51dbd7..f972a32685726d115ad4b0fb469278e4415e4d1f 100644 --- a/enzevalos_iphone/persistentData/PublicKeyRecord.swift +++ b/enzevalos_iphone/persistentData/PublicKeyRecord.swift @@ -39,9 +39,9 @@ extension PublicKeyRecord { if discovery == nil { discovery = Date() } - - self.origin = property.origin.rawValue - + if let origin = property.origin?.rawValue { + self.origin = origin + } if let lastSeen = property.lastSeenInAutocryptHeader { if let last = self.lastSeenInAutocryptHeader { if last < lastSeen { diff --git a/enzevalos_iphoneTests/AutocryptTest.swift b/enzevalos_iphoneTests/AutocryptTest.swift index abf8e50ef71c14c95750253620a4e78428bf2451..d5072f6c8432de15c8a4d3aac58cfa656301ecb6 100644 --- a/enzevalos_iphoneTests/AutocryptTest.swift +++ b/enzevalos_iphoneTests/AutocryptTest.swift @@ -103,6 +103,40 @@ class AutocryptTest: XCTestCase { } } + func testSelfCreatedHeader() { + print("My keyID: \(userKeyID)") + guard let customHeader = Autocrypt.customAutocryptHeader else { + XCTFail("No autocrypt...") + return + } + // Create custom header + var headerString = "" + headerString += "From: " + userAdr + "\r\n" + headerString += "To: " + "alice@example.com" + "\r\n" + headerString += customHeader + headerString += "Message-ID: " + "202029293239sisdj292323" + "\r\n" + headerString += "Subject: " + "subject" + "\r\n" + + let mail = headerString + "Hello World" + guard let data = mail.data(using: .utf8) else { + XCTFail("No mail data...") + return + } + + let inc = IncomingMail(rawData: data, uID: 0, folderPath: "Test", flags: .seen) + let prop = inc.export() + XCTAssertEqual(prop.autocryptHeaderKey.count, 1) + guard let key = prop.autocryptHeaderKey.first else { + XCTFail("No key...") + return + } + XCTAssertEqual(key.fingerprint, userKeyID) + XCTAssertEqual(key.cryptoProtocol, .PGP) + XCTAssertEqual(key.origin, .AutocryptHeader) + XCTAssertEqual(key.preferEncryption, .MUTUAL) + XCTAssertEqual(key.usedAddresses?.first?.email, userAdr) + } + func testAutocryptHeader(){ let outmail = OutgoingMail(toEntrys: ["alice@example.com"], ccEntrys: [], bccEntrys: [], subject: "subject", textContent: "Body", htmlContent: nil) if let parser = MCOMessageParser(data: outmail.plainData), let _ = pgp.exportKey(id: userKeyID, isSecretkey: false, autocrypt: false) { diff --git a/enzevalos_iphoneTests/CryptoTests.swift b/enzevalos_iphoneTests/CryptoTests.swift index e8ff19b498299b1a380b52daf2788f44222b131d..953b95544dbe345e1e815e1df885aafaf0bb809c 100644 --- a/enzevalos_iphoneTests/CryptoTests.swift +++ b/enzevalos_iphoneTests/CryptoTests.swift @@ -611,7 +611,7 @@ class CryptoTests: XCTestCase { let subject = "Test mail" let folder = FolderProperties(delimiter: nil, uidValidity: nil, lastUpdate: nil, maxUID: nil, minUID: nil, path: "Testfolder", parent: nil, children: nil) - let mail = MailProperties(messageID: "dasdads", subject: subject, date: Date(), flags: 0, from: from, to: to, cc: [], bcc: [], folder: folder, body: body, attachments: [], signatureState: sigState.rawValue, encryptionState: encState.rawValue, signatureKey: nil, decryptionKey: nil, autocryptHeaderKey: nil, attachedPublicKeys: [], attachedSecretKeys: []) + let mail = MailProperties(messageID: "dasdads", subject: subject, date: Date(), flags: 0, from: from, to: to, cc: [], bcc: [], folder: folder, body: body, attachments: [], signatureState: sigState.rawValue, encryptionState: encState.rawValue, signatureKey: nil, decryptionKey: nil, autocryptHeaderKey: [], attachedPublicKeys: [], attachedSecretKeys: []) let importExpectation = expectation(description: "Import email") PersitentDataProvider.dataProvider.importNewData(from: [mail], completionHandler: {error in diff --git a/enzevalos_iphoneTests/GeneratedMocks.swift b/enzevalos_iphoneTests/GeneratedMocks.swift index a8706487975d656dddc47e9c45802986517b57b1..90fd44ce27f9cb8e4141339f0d7a08a2c7135541 100644 --- a/enzevalos_iphoneTests/GeneratedMocks.swift +++ b/enzevalos_iphoneTests/GeneratedMocks.swift @@ -1,4 +1,4 @@ -// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationModel.swift at 2021-03-01 09:59:44 +0000 +// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationModel.swift at 2021-03-15 16:18:38 +0000 // // AuthenticationModel.swift @@ -654,7 +654,7 @@ import Foundation } -// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationViewModel.swift at 2021-03-01 09:59:44 +0000 +// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationViewModel.swift at 2021-03-15 16:18:38 +0000 // // AuthenticationViewModel.swift diff --git a/enzevalos_iphoneTests/MailTest.swift b/enzevalos_iphoneTests/MailTest.swift index 465de0af349addf011fb22a5cc66f8c989efb57e..0e6a2b95e0ca7cfff9b4c918f7fd03e704d2110e 100644 --- a/enzevalos_iphoneTests/MailTest.swift +++ b/enzevalos_iphoneTests/MailTest.swift @@ -504,7 +504,7 @@ n1O3czuVl7rPXrJn0A/MVI2ReKOQeIAYMg== return (aliceKeyId, bobKeyId) } - static func compareAdrs(adrs1: [String], adrs2: [MailAddress]) -> Bool{ + static func compareAdrs(adrs1: [String], adrs2: [AddressRecord]) -> Bool{ for adr in adrs1 { var found = false for adr2 in adrs2 { diff --git a/enzevalos_iphoneTests/phishing/MailComparisonTests.swift b/enzevalos_iphoneTests/phishing/MailComparisonTests.swift index 89c12a11601ea21bb4ee0a9b3afbfbcd403b8918..bf017274fd92f27d75eb83b4951c143e9354b0be 100644 --- a/enzevalos_iphoneTests/phishing/MailComparisonTests.swift +++ b/enzevalos_iphoneTests/phishing/MailComparisonTests.swift @@ -12,18 +12,18 @@ import Contacts class MailComparisonTests: XCTestCase { let userAdr = "alice@example.com" let userName = "alice" - var user: MCOAddress = MCOAddress.init(displayName: "alice", mailbox: "alice@example.com") - let userAdr_1 = "bob@enzevalos.de" + var user = AddressProperties(email: "alice@example.com", name: "alice") + let userAdr_1 = "bob@example.de" let userName_1 = "bob" - var user_1: MCOAddress = MCOAddress.init(displayName: "bob", mailbox: "bob@enzevalos.de") + var user_1 = AddressProperties(email: "bob@example.com", name: "bob") let userAdr_2 = "carl@test.com" let userName_2 = "carl" - var user_2: MCOAddress = MCOAddress.init(displayName: "Carl", mailbox: "carl@test.com") + var user_2 = AddressProperties(email: "carl@example.com", name: "carl") let userAddr_3_1 = "david@123.com" let userAddr_3_2 = "david@1234.com" let userName_3 = "david" - var user_3_1: MCOAddress = MCOAddress.init(displayName: "david", mailbox: "david@123.com") - var user_3_2: MCOAddress = MCOAddress.init(displayName: "david", mailbox: "david@1234.com") + var user_3_1 = AddressProperties(email: "david@123.com", name: "david") + var user_3_2 = AddressProperties(email: "david@1234.com", name: "david") private func deleteAll() { let provider = PersitentDataProvider() @@ -44,8 +44,6 @@ class MailComparisonTests: XCTestCase { override func setUp() { super.setUp() deleteAll() - //TODO XCTAssertEqual(datahandler.getContacts().count, 0) - //TODO XCTAssertEqual(datahandler.getAddresses().count, 0) } override func tearDown() { @@ -53,27 +51,36 @@ class MailComparisonTests: XCTestCase { super.tearDown() } - func testMail(from: MCOAddress, to: [MCOAddress], cc: [MCOAddress], bcc: [MCOAddress], flags: MCOMessageFlag = MCOMessageFlag.init(rawValue: 0), folder: String = "INBOX", date: Date = Date(timeIntervalSince1970: TimeInterval(arc4random())), cryptoObject: CryptoObject? = nil, body: String = String.random(length: 20)) -> MailRecord? { + func testMail(from: AddressProperties, to: [AddressProperties], cc: [AddressProperties], bcc: [AddressProperties], flags: MCOMessageFlag = MCOMessageFlag.init(rawValue: 0), folder: FolderProperties = FolderProperties(delimiter: nil, uidValidity: nil, lastUpdate: nil, maxUID: nil, minUID: nil, path: "Inbox", parent: nil, children: nil), date: Date = Date(timeIntervalSince1970: TimeInterval(arc4random())), cryptoObject: CryptoObject? = nil, body: String = String.random(length: 20)) -> MailProperties { let subject = String.random(length: 20) let id = UInt64(arc4random()) var body = body + var sigState = SignatureState.NoSignature + var encState = EncryptionState.NoEncryption + if let cryptoObject = cryptoObject { + sigState = cryptoObject.signatureState + encState = cryptoObject.encryptionState + } if let decryptedBody = cryptoObject?.decryptedText { body = decryptedBody } - var mail: MailRecord? = nil - //TODO mail = datahandler.createMail(id, sender: from, receivers: to, cc: cc, time: date, received: true, subject: subject, body: body, flags: flags, record: nil, autocrypt: nil, decryptedData: cryptoObject, folderPath: folder, secretKey: nil, encryptedBody: cryptoObject?.chiperString) - + let mail = MailProperties(messageID: "\(id)", subject: subject, date: date, flags: Int16(flags.rawValue), from: from, to: to, cc: cc, bcc: bcc, folder: folder, body: body, attachments: [], signatureState: sigState.rawValue, encryptionState: encState.rawValue, signatureKey: nil, decryptionKey: nil, autocryptHeaderKey: [], attachedPublicKeys: [], attachedSecretKeys: []) + + PersitentDataProvider.dataProvider.importNewData(from: [mail], completionHandler: { + error in + print("Done!") + }) return mail } - private func createAddressBook(_ senders: [MCOAddress]) { + private func createAddressBook(_ senders: [AddressProperties]) { for sender in senders { let con = CNMutableContact() - let value = sender.mailbox! as AnyObject + let value = sender.email as AnyObject - let name = sender.displayName + let name = sender.name let nameArray = name!.split(separator: " ").map(String.init) con.givenName = nameArray.first! con.emailAddresses.append(CNLabeledValue(label: CNLabelOther, value: value as! NSString)) @@ -85,10 +92,10 @@ class MailComparisonTests: XCTestCase { } } - private func resetAddressBook(_ senders: [MCOAddress]) { + private func resetAddressBook(_ senders: [AddressProperties]) { for sender in senders { do { - let name = sender.displayName! + let name = sender.name ?? "" let predicate = CNContact.predicateForContacts(matchingName: name) let store = CNContactStore() let contacts = try store.unifiedContacts(matching: predicate,keysToFetch: [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor]) @@ -102,7 +109,7 @@ class MailComparisonTests: XCTestCase { } } - func testCompareSenderToContacts () { + func testCompareSenderToContacts() { let mail_1 = testMail(from: user, to: [], cc: [], bcc: []) let mail_2 = testMail(from: user_1, to: [], cc: [], bcc: []) let mail_3 = testMail(from: user_2, to: [], cc: [], bcc: []) @@ -110,20 +117,27 @@ class MailComparisonTests: XCTestCase { let mail_5 = testMail(from: user_3_1, to: [], cc: [], bcc: []) let mail_6 = testMail(from: user_3_2, to: [], cc: [], bcc: []) - let mailsInInbox: [MailRecord] = [mail_1!, mail_2!, mail_3!, mail_4!, mail_5!, mail_6!] + let mailsInInbox: [MailProperties] = [mail_1, mail_2, mail_3, mail_4, mail_5, mail_6] createAddressBook([user]) - XCTAssertEqual(ResultCompareSenderToContacts.isContact, mailsInInbox[0].fromAddress?.email.compareSenderToContacts()) - XCTAssertEqual(ResultCompareSenderToContacts.Unknown, mailsInInbox[1].fromAddress?.email.compareSenderToContacts()) - XCTAssertEqual(ResultCompareSenderToContacts.isSender, mailsInInbox[2].fromAddress?.email.compareSenderToContacts()) + XCTAssertEqual(ResultCompareSenderToContacts.isContact, mailsInInbox[0].from?.email.compareSenderToContacts()) + XCTAssertEqual(ResultCompareSenderToContacts.Unknown, mailsInInbox[1].from?.email.compareSenderToContacts()) + XCTAssertEqual(ResultCompareSenderToContacts.isSender, mailsInInbox[2].from?.email.compareSenderToContacts()) - XCTAssertEqual(ResultCompareSenderToContacts.OnlyIdentity, mailsInInbox[4].fromAddress?.email.compareSenderToContacts()) // userAddr_3_2 + XCTAssertEqual(ResultCompareSenderToContacts.OnlyIdentity, mailsInInbox[4].from?.email.compareSenderToContacts()) // userAddr_3_2 // The created user has to be deleted after the test resetAddressBook([user]) } + + func testContactHandler() { + createAddressBook([user]) + let res = ContactHandler().findContactBy(email: user.email) + XCTAssertEqual(res.count, 1) + resetAddressBook([user]) + } } diff --git a/enzevalos_iphoneTests/testMails/signedSMIMEfromMac 2.eml b/enzevalos_iphoneTests/testMails/signedSMIMEfromMac 2.eml new file mode 100644 index 0000000000000000000000000000000000000000..91f00a0843a257cefbb83336e307cd4500b7af8e --- /dev/null +++ b/enzevalos_iphoneTests/testMails/signedSMIMEfromMac 2.eml @@ -0,0 +1,121 @@ +From: Oliver Wiese <oliver.wiese@fu-berlin.de> +Content-Type: multipart/signed; + boundary="Apple-Mail=_58C9D0E1-2B0A-4882-8F02-411E8FCB5BF0"; + protocol="application/pkcs7-signature"; + micalg=sha-256 +Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.4\)) +Subject: A signed smime mail from mac +X-Universally-Unique-Identifier: 4DCBCCEE-E252-4369-B8DA-8390C62F2AF3 +Message-Id: <D1D41054-2FAC-4B10-B692-5F3D296BA1E9@fu-berlin.de> +Date: Tue, 16 Feb 2021 17:16:58 +0100 +To: bob@enzevalos.de + + +--Apple-Mail=_58C9D0E1-2B0A-4882-8F02-411E8FCB5BF0 +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +A signed smime mail from mac +--Apple-Mail=_58C9D0E1-2B0A-4882-8F02-411E8FCB5BF0 +Content-Disposition: attachment; + filename=smime.p7s +Content-Type: application/pkcs7-signature; + name=smime.p7s +Content-Transfer-Encoding: base64 + +MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwEAAKCCEQcw +ggUSMIID+qADAgECAgkA4wvV+K8l2YEwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAkRFMSsw +KQYDVQQKDCJULVN5c3RlbXMgRW50ZXJwcmlzZSBTZXJ2aWNlcyBHbWJIMR8wHQYDVQQLDBZULVN5 +c3RlbXMgVHJ1c3QgQ2VudGVyMSUwIwYDVQQDDBxULVRlbGVTZWMgR2xvYmFsUm9vdCBDbGFzcyAy +MB4XDTE2MDIyMjEzMzgyMloXDTMxMDIyMjIzNTk1OVowgZUxCzAJBgNVBAYTAkRFMUUwQwYDVQQK +EzxWZXJlaW4genVyIEZvZXJkZXJ1bmcgZWluZXMgRGV1dHNjaGVuIEZvcnNjaHVuZ3NuZXR6ZXMg +ZS4gVi4xEDAOBgNVBAsTB0RGTi1QS0kxLTArBgNVBAMTJERGTi1WZXJlaW4gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkgMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMtg1/9moUHN0vqH +l4pzq5lN6mc5WqFggEcVToyVsuXPztNXS43O+FZsFVV2B+pG/cgDRWM+cNSrVICxI5y+NyipCf8F +XRgPxJiZN7Mg9mZ4F4fCnQ7MSjLnFp2uDo0peQcAIFTcFV9Kltd4tjTTwXS1nem/wHdN6r1ZB+Ba +L2w8pQDcNb1lDY9/Mm3yWmpLYgHurDg0WUU2SQXaeMpqbVvAgWsRzNI8qIv4cRrKO+KA3Ra0Z3qL +NupOkSk9s1FcragMvp0049ENF4N1xDkesJQLEvHVaY4l9Lg9K7/AjsMeO6W/VRCrKq4Xl14zzsjz +9AkH4wKGMUZrAcUQDBHHWekCAwEAAaOCAXQwggFwMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +k+PYMiba1fFKpZFK4OpL4qIMz+EwHwYDVR0jBBgwFoAUv1kgNgB5oKAia4zV8mHSuCzLgkowEgYD +VR0TAQH/BAgwBgEB/wIBAjAzBgNVHSAELDAqMA8GDSsGAQQBga0hgiwBAQQwDQYLKwYBBAGBrSGC +LB4wCAYGZ4EMAQICMEwGA1UdHwRFMEMwQaA/oD2GO2h0dHA6Ly9wa2kwMzM2LnRlbGVzZWMuZGUv +cmwvVGVsZVNlY19HbG9iYWxSb290X0NsYXNzXzIuY3JsMIGGBggrBgEFBQcBAQR6MHgwLAYIKwYB +BQUHMAGGIGh0dHA6Ly9vY3NwMDMzNi50ZWxlc2VjLmRlL29jc3ByMEgGCCsGAQUFBzAChjxodHRw +Oi8vcGtpMDMzNi50ZWxlc2VjLmRlL2NydC9UZWxlU2VjX0dsb2JhbFJvb3RfQ2xhc3NfMi5jZXIw +DQYJKoZIhvcNAQELBQADggEBAIcL/z4Cm2XIVi3WO5qYi3FP2ropqiH5Ri71sqQPrhE4eTizDnS6 +dl2e6BiClmLbTDPo3flq3zK9LExHYFV/53RrtCyD2HlrtrdNUAtmB7Xts5et6u5/MOaZ/SLick0+ +hFvu+c+Z6n/XUjkurJgARH5pO7917tALOxrN5fcPImxHhPalR6D90Bo0fa3SPXez7vTXTf/D6OWS +T1k+kEcQSrCFWMBvf/iu7QhCnh7U3xQuTY+8npTD5+32GPg8SecmqKc22CzeIs2LgtjZeOJVEqM7 +h0S2EQvVDFKvaYwPBt/QolOLV5h7z/0HJPT8vcP9SpIClxvyt7bPZYoaorVyGTkwggWsMIIElKAD +AgECAgcbY7rQHiw9MA0GCSqGSIb3DQEBCwUAMIGVMQswCQYDVQQGEwJERTFFMEMGA1UEChM8VmVy +ZWluIHp1ciBGb2VyZGVydW5nIGVpbmVzIERldXRzY2hlbiBGb3JzY2h1bmdzbmV0emVzIGUuIFYu +MRAwDgYDVQQLEwdERk4tUEtJMS0wKwYDVQQDEyRERk4tVmVyZWluIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IDIwHhcNMTYwNTI0MTEzODQwWhcNMzEwMjIyMjM1OTU5WjCBjTELMAkGA1UEBhMCREUx +RTBDBgNVBAoMPFZlcmVpbiB6dXIgRm9lcmRlcnVuZyBlaW5lcyBEZXV0c2NoZW4gRm9yc2NodW5n +c25ldHplcyBlLiBWLjEQMA4GA1UECwwHREZOLVBLSTElMCMGA1UEAwwcREZOLVZlcmVpbiBHbG9i +YWwgSXNzdWluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ07eRxH3h+Gy8Zp +1xCeOdfZojDbchwFfylfS2jxrRnWTOFrG7ELf6Gr4HuLi9gtzm6IOhDuV+UefwRRNuu6cG1joL6W +LkDh0YNMZj0cZGnlm6Stcq5oOVGHecwX064vXWNxSzl660Knl5BpBb+Q/6RAcL0D57+eGIgfn5mI +TQ5HjUhfZZkQ0tkqSe3BuS0dnxLLFdM/fx5ULzquk1enfnjK1UriGuXtQX1TX8izKvWKMKztFwUk +P7agCwf9TRqaA1KgNpzeJIdl5Of6x5ZzJBTN0OgbaJ4YWa52fvfRCng8h0uwN89Tyjo4EPPLR22M +ZD08WkVKusqAfLjz56dMTM0CAwEAAaOCAgUwggIBMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYDVR0P +AQH/BAQDAgEGMCkGA1UdIAQiMCAwDQYLKwYBBAGBrSGCLB4wDwYNKwYBBAGBrSGCLAEBBDAdBgNV +HQ4EFgQUazqYi/nyU4na4K2yMh4JH+iqO3QwHwYDVR0jBBgwFoAUk+PYMiba1fFKpZFK4OpL4qIM +z+EwgY8GA1UdHwSBhzCBhDBAoD6gPIY6aHR0cDovL2NkcDEucGNhLmRmbi5kZS9nbG9iYWwtcm9v +dC1nMi1jYS9wdWIvY3JsL2NhY3JsLmNybDBAoD6gPIY6aHR0cDovL2NkcDIucGNhLmRmbi5kZS9n +bG9iYWwtcm9vdC1nMi1jYS9wdWIvY3JsL2NhY3JsLmNybDCB3QYIKwYBBQUHAQEEgdAwgc0wMwYI +KwYBBQUHMAGGJ2h0dHA6Ly9vY3NwLnBjYS5kZm4uZGUvT0NTUC1TZXJ2ZXIvT0NTUDBKBggrBgEF +BQcwAoY+aHR0cDovL2NkcDEucGNhLmRmbi5kZS9nbG9iYWwtcm9vdC1nMi1jYS9wdWIvY2FjZXJ0 +L2NhY2VydC5jcnQwSgYIKwYBBQUHMAKGPmh0dHA6Ly9jZHAyLnBjYS5kZm4uZGUvZ2xvYmFsLXJv +b3QtZzItY2EvcHViL2NhY2VydC9jYWNlcnQuY3J0MA0GCSqGSIb3DQEBCwUAA4IBAQCBeEWkTqR/ +DlXwCbFqPnjMaDWpHPOVnj/z+N9rOHeJLI21rT7H8pTNoAauusyosa0zCLYkhmI2THhuUPDVbmCN +T1IxQ5dGdfBi5G5mUcFCMWdQ5UnnOR7Ln8qGSN4IFP8VSytmm6A4nwDO/afr0X9XLchMX9wQEZc+ +lgQCXISoKTlslPwQkgZ7nu7YRrQbtQMMONncsKk/cQYLsgMHM8KNSGMlJTx6e1du94oFOO+4oK4v +9NsH1VuEGMGpuEvObJAaguS5Pfp38dIfMwK/U+d2+dwmJUFvL6Yb+qQTkPp8ftkLYF3sv8pBoGH7 +EUkp2KgtdRXYShjqFu9VNCIaE40GMIIGPTCCBSWgAwIBAgIMHquxIGDgCGtc8ZZZMA0GCSqGSIb3 +DQEBCwUAMIGNMQswCQYDVQQGEwJERTFFMEMGA1UECgw8VmVyZWluIHp1ciBGb2VyZGVydW5nIGVp +bmVzIERldXRzY2hlbiBGb3JzY2h1bmdzbmV0emVzIGUuIFYuMRAwDgYDVQQLDAdERk4tUEtJMSUw +IwYDVQQDDBxERk4tVmVyZWluIEdsb2JhbCBJc3N1aW5nIENBMB4XDTE4MDIyMDE0NDEyMloXDTIx +MDIxOTE0NDEyMlowgZoxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl +cmxpbjEiMCAGA1UECgwZRnJlaWUgVW5pdmVyc2l0YWV0IEJlcmxpbjEuMCwGA1UECwwlRmFjaGJl +cmVpY2ggTWF0aGVtYXRpayB1bmQgSW5mb3JtYXRpazEVMBMGA1UEAwwMT2xpdmVyIFdpZXNlMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx25g+X495DeptnwCaQ6cykfU3s2nICuN+XOA +Jlvu2alLN2qaySL2vmumySejVk2jZxC3Is5I/3GrYL6uCpLjp0w7LeEkmrfRGFYV1cMOMtKz+exv +M+xplyYf4ibVYPhPGR3oHy3VDdd1QQolqmADdS4MDeOHBknEqaWDvVSkSWt2Jm5QmnVuvEDmppsA +c4C3w8IR1MOU3TnVphlIrDF6ZrMxKvxzCrFEby8jE6paipgS+ZfZxRtCaqZUWW3Vu5CWWyz1CLOu +Ff0FZOX6uG8RdBeBm7NUagg8UXkDCKX08bMIYfShz+IoCy0aHV7Ks5I3+N6/2pHEmoWm/qpdkPta +fQIDAQABo4ICjDCCAogwQAYDVR0gBDkwNzAPBg0rBgEEAYGtIYIsAQEEMBEGDysGAQQBga0hgiwB +AQQDBzARBg8rBgEEAYGtIYIsAgEEAwcwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0l +BBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMB0GA1UdDgQWBBTZ8ytnI7fxN4uilTbn+lUG1l6R0jAf +BgNVHSMEGDAWgBRrOpiL+fJTidrgrbIyHgkf6Ko7dDBcBgNVHREEVTBTgRlvbGl2ZXIud2llc2VA +ZnUtYmVybGluLmRlgRt3aWVzZW9saUB6ZWRhdC5mdS1iZXJsaW4uZGWBGU9saXZlci5XaWVzZUBm +dS1iZXJsaW4uZGUwgY0GA1UdHwSBhTCBgjA/oD2gO4Y5aHR0cDovL2NkcDEucGNhLmRmbi5kZS9k +Zm4tY2EtZ2xvYmFsLWcyL3B1Yi9jcmwvY2FjcmwuY3JsMD+gPaA7hjlodHRwOi8vY2RwMi5wY2Eu +ZGZuLmRlL2Rmbi1jYS1nbG9iYWwtZzIvcHViL2NybC9jYWNybC5jcmwwgdsGCCsGAQUFBwEBBIHO +MIHLMDMGCCsGAQUFBzABhidodHRwOi8vb2NzcC5wY2EuZGZuLmRlL09DU1AtU2VydmVyL09DU1Aw +SQYIKwYBBQUHMAKGPWh0dHA6Ly9jZHAxLnBjYS5kZm4uZGUvZGZuLWNhLWdsb2JhbC1nMi9wdWIv +Y2FjZXJ0L2NhY2VydC5jcnQwSQYIKwYBBQUHMAKGPWh0dHA6Ly9jZHAyLnBjYS5kZm4uZGUvZGZu +LWNhLWdsb2JhbC1nMi9wdWIvY2FjZXJ0L2NhY2VydC5jcnQwDQYJKoZIhvcNAQELBQADggEBAIZ4 +78RroIuTMr5QvBVINS9oX6HFIOHCPtSuuKRMpb6OHCmyye0NySJAVtK9ozDzomkShvMzMm5SmAoG +xubcqgb5ovg8eixwDvEj+QlKQ8hQHUNoYf4ExNeU+KDE0pSE6AM1GgnRB34Xtjpk/d+6nA6JKqxz +VSH00K3dSzVtrDdgnAdr4su8pTtgYNL2hlTzqkSj0MdzcyBq7PvmVlSE60umivECuQt+Sig6HEt2 +e2bgQ52ZVvKTpQ8oTj0+Mfw2ul2eYyRcLEz9Y2/GpwLXai5QQbClPmPRo1z0mFoYYe2R8QiwHHgS +11RI6lHw4wUHox0PrfDb8vZlz2jafZQendUxggOdMIIDmQIBATCBnjCBjTELMAkGA1UEBhMCREUx +RTBDBgNVBAoMPFZlcmVpbiB6dXIgRm9lcmRlcnVuZyBlaW5lcyBEZXV0c2NoZW4gRm9yc2NodW5n +c25ldHplcyBlLiBWLjEQMA4GA1UECwwHREZOLVBLSTElMCMGA1UEAwwcREZOLVZlcmVpbiBHbG9i +YWwgSXNzdWluZyBDQQIMHquxIGDgCGtc8ZZZMA0GCWCGSAFlAwQCAQUAoIIBzzAYBgkqhkiG9w0B +CQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMTAyMTYxNjE2NThaMC8GCSqGSIb3DQEJ +BDEiBCC8Fytiv1ZpmEb8bvmg27rVyebYhhu4n7CvuB65krlt7DCBrwYJKwYBBAGCNxAEMYGhMIGe +MIGNMQswCQYDVQQGEwJERTFFMEMGA1UECgw8VmVyZWluIHp1ciBGb2VyZGVydW5nIGVpbmVzIERl +dXRzY2hlbiBGb3JzY2h1bmdzbmV0emVzIGUuIFYuMRAwDgYDVQQLDAdERk4tUEtJMSUwIwYDVQQD +DBxERk4tVmVyZWluIEdsb2JhbCBJc3N1aW5nIENBAgweq7EgYOAIa1zxllkwgbEGCyqGSIb3DQEJ +EAILMYGhoIGeMIGNMQswCQYDVQQGEwJERTFFMEMGA1UECgw8VmVyZWluIHp1ciBGb2VyZGVydW5n +IGVpbmVzIERldXRzY2hlbiBGb3JzY2h1bmdzbmV0emVzIGUuIFYuMRAwDgYDVQQLDAdERk4tUEtJ +MSUwIwYDVQQDDBxERk4tVmVyZWluIEdsb2JhbCBJc3N1aW5nIENBAgweq7EgYOAIa1zxllkwDQYJ +KoZIhvcNAQEBBQAEggEAMT5sbk0eX0B6kJjLa22vWULGUjKIo/klAOLzeTbejIdGy+kRxZP3Ztxy +cROX8JxHGMPslJ9lmnbmIYwCqb7JgIZjyelJNDESzPzo12krkhH0t8J3tUFC/TyKCQg1JBRNdT+L +08/7/xYJL7DOG1CXTiIQi7P29QpCGCisfuEKJ8ac3cZrT81EIfGnEoIAKam+RFlE4TDAZEBF8qgm +s6pxT8aSUjMnMP8ZtTHQpNbUsMqLkilXYsnSREleAbqptItWbJ+qsGJMcC4wc28tNGOLEsdYjGsH +eM+AIln6HACsMCpqXSkxgkc7T7F7DTlb+FwGe+UyaZOj1O3LzoS+b8jijAAAAAAAAA== +--Apple-Mail=_58C9D0E1-2B0A-4882-8F02-411E8FCB5BF0-- diff --git a/enzevalos_iphoneTests/testMails/signedSMIMEfromMac.eml b/enzevalos_iphoneTests/testMails/signedSMIMEfromMac.eml index 91f00a0843a257cefbb83336e307cd4500b7af8e..cedd9975e15a43792ab425930a2f688c15dd82d7 100644 --- a/enzevalos_iphoneTests/testMails/signedSMIMEfromMac.eml +++ b/enzevalos_iphoneTests/testMails/signedSMIMEfromMac.eml @@ -1,30 +1,30 @@ From: Oliver Wiese <oliver.wiese@fu-berlin.de> Content-Type: multipart/signed; - boundary="Apple-Mail=_58C9D0E1-2B0A-4882-8F02-411E8FCB5BF0"; + boundary="Apple-Mail=_9BE88FFD-313B-4C05-B770-19F608BDA482"; protocol="application/pkcs7-signature"; micalg=sha-256 Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.4\)) -Subject: A signed smime mail from mac -X-Universally-Unique-Identifier: 4DCBCCEE-E252-4369-B8DA-8390C62F2AF3 -Message-Id: <D1D41054-2FAC-4B10-B692-5F3D296BA1E9@fu-berlin.de> -Date: Tue, 16 Feb 2021 17:16:58 +0100 +Subject: Signed SMIME Mail from Mac +X-Universally-Unique-Identifier: 446C1C0E-D181-4FE7-A3D7-EFE6936F1D9F +Message-Id: <2A97340F-AB0E-4981-9736-8EA9AD278B41@fu-berlin.de> +Date: Wed, 10 Mar 2021 17:04:27 +0100 To: bob@enzevalos.de ---Apple-Mail=_58C9D0E1-2B0A-4882-8F02-411E8FCB5BF0 +--Apple-Mail=_9BE88FFD-313B-4C05-B770-19F608BDA482 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=us-ascii -A signed smime mail from mac ---Apple-Mail=_58C9D0E1-2B0A-4882-8F02-411E8FCB5BF0 +Signed SMIME Mail from Mac +--Apple-Mail=_9BE88FFD-313B-4C05-B770-19F608BDA482 Content-Disposition: attachment; filename=smime.p7s Content-Type: application/pkcs7-signature; name=smime.p7s Content-Transfer-Encoding: base64 -MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwEAAKCCEQcw +MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwEAAKCCESYw ggUSMIID+qADAgECAgkA4wvV+K8l2YEwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAkRFMSsw KQYDVQQKDCJULVN5c3RlbXMgRW50ZXJwcmlzZSBTZXJ2aWNlcyBHbWJIMR8wHQYDVQQLDBZULVN5 c3RlbXMgVHJ1c3QgQ2VudGVyMSUwIwYDVQQDDBxULVRlbGVTZWMgR2xvYmFsUm9vdCBDbGFzcyAy @@ -73,49 +73,50 @@ DlXwCbFqPnjMaDWpHPOVnj/z+N9rOHeJLI21rT7H8pTNoAauusyosa0zCLYkhmI2THhuUPDVbmCN T1IxQ5dGdfBi5G5mUcFCMWdQ5UnnOR7Ln8qGSN4IFP8VSytmm6A4nwDO/afr0X9XLchMX9wQEZc+ lgQCXISoKTlslPwQkgZ7nu7YRrQbtQMMONncsKk/cQYLsgMHM8KNSGMlJTx6e1du94oFOO+4oK4v 9NsH1VuEGMGpuEvObJAaguS5Pfp38dIfMwK/U+d2+dwmJUFvL6Yb+qQTkPp8ftkLYF3sv8pBoGH7 -EUkp2KgtdRXYShjqFu9VNCIaE40GMIIGPTCCBSWgAwIBAgIMHquxIGDgCGtc8ZZZMA0GCSqGSIb3 +EUkp2KgtdRXYShjqFu9VNCIaE40GMIIGXDCCBUSgAwIBAgIMJFhrl196HHUl2Ey0MA0GCSqGSIb3 DQEBCwUAMIGNMQswCQYDVQQGEwJERTFFMEMGA1UECgw8VmVyZWluIHp1ciBGb2VyZGVydW5nIGVp bmVzIERldXRzY2hlbiBGb3JzY2h1bmdzbmV0emVzIGUuIFYuMRAwDgYDVQQLDAdERk4tUEtJMSUw -IwYDVQQDDBxERk4tVmVyZWluIEdsb2JhbCBJc3N1aW5nIENBMB4XDTE4MDIyMDE0NDEyMloXDTIx -MDIxOTE0NDEyMlowgZoxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl +IwYDVQQDDBxERk4tVmVyZWluIEdsb2JhbCBJc3N1aW5nIENBMB4XDTIxMDIyNjEyNDgwOVoXDTI0 +MDIyNjEyNDgwOVowgbsxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl cmxpbjEiMCAGA1UECgwZRnJlaWUgVW5pdmVyc2l0YWV0IEJlcmxpbjEuMCwGA1UECwwlRmFjaGJl -cmVpY2ggTWF0aGVtYXRpayB1bmQgSW5mb3JtYXRpazEVMBMGA1UEAwwMT2xpdmVyIFdpZXNlMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx25g+X495DeptnwCaQ6cykfU3s2nICuN+XOA -Jlvu2alLN2qaySL2vmumySejVk2jZxC3Is5I/3GrYL6uCpLjp0w7LeEkmrfRGFYV1cMOMtKz+exv -M+xplyYf4ibVYPhPGR3oHy3VDdd1QQolqmADdS4MDeOHBknEqaWDvVSkSWt2Jm5QmnVuvEDmppsA -c4C3w8IR1MOU3TnVphlIrDF6ZrMxKvxzCrFEby8jE6paipgS+ZfZxRtCaqZUWW3Vu5CWWyz1CLOu -Ff0FZOX6uG8RdBeBm7NUagg8UXkDCKX08bMIYfShz+IoCy0aHV7Ks5I3+N6/2pHEmoWm/qpdkPta -fQIDAQABo4ICjDCCAogwQAYDVR0gBDkwNzAPBg0rBgEEAYGtIYIsAQEEMBEGDysGAQQBga0hgiwB -AQQDBzARBg8rBgEEAYGtIYIsAgEEAwcwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0l -BBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMB0GA1UdDgQWBBTZ8ytnI7fxN4uilTbn+lUG1l6R0jAf -BgNVHSMEGDAWgBRrOpiL+fJTidrgrbIyHgkf6Ko7dDBcBgNVHREEVTBTgRlvbGl2ZXIud2llc2VA -ZnUtYmVybGluLmRlgRt3aWVzZW9saUB6ZWRhdC5mdS1iZXJsaW4uZGWBGU9saXZlci5XaWVzZUBm -dS1iZXJsaW4uZGUwgY0GA1UdHwSBhTCBgjA/oD2gO4Y5aHR0cDovL2NkcDEucGNhLmRmbi5kZS9k -Zm4tY2EtZ2xvYmFsLWcyL3B1Yi9jcmwvY2FjcmwuY3JsMD+gPaA7hjlodHRwOi8vY2RwMi5wY2Eu -ZGZuLmRlL2Rmbi1jYS1nbG9iYWwtZzIvcHViL2NybC9jYWNybC5jcmwwgdsGCCsGAQUFBwEBBIHO -MIHLMDMGCCsGAQUFBzABhidodHRwOi8vb2NzcC5wY2EuZGZuLmRlL09DU1AtU2VydmVyL09DU1Aw -SQYIKwYBBQUHMAKGPWh0dHA6Ly9jZHAxLnBjYS5kZm4uZGUvZGZuLWNhLWdsb2JhbC1nMi9wdWIv -Y2FjZXJ0L2NhY2VydC5jcnQwSQYIKwYBBQUHMAKGPWh0dHA6Ly9jZHAyLnBjYS5kZm4uZGUvZGZu -LWNhLWdsb2JhbC1nMi9wdWIvY2FjZXJ0L2NhY2VydC5jcnQwDQYJKoZIhvcNAQELBQADggEBAIZ4 -78RroIuTMr5QvBVINS9oX6HFIOHCPtSuuKRMpb6OHCmyye0NySJAVtK9ozDzomkShvMzMm5SmAoG -xubcqgb5ovg8eixwDvEj+QlKQ8hQHUNoYf4ExNeU+KDE0pSE6AM1GgnRB34Xtjpk/d+6nA6JKqxz -VSH00K3dSzVtrDdgnAdr4su8pTtgYNL2hlTzqkSj0MdzcyBq7PvmVlSE60umivECuQt+Sig6HEt2 -e2bgQ52ZVvKTpQ8oTj0+Mfw2ul2eYyRcLEz9Y2/GpwLXai5QQbClPmPRo1z0mFoYYe2R8QiwHHgS -11RI6lHw4wUHox0PrfDb8vZlz2jafZQendUxggOdMIIDmQIBATCBnjCBjTELMAkGA1UEBhMCREUx +cmVpY2ggTWF0aGVtYXRpayB1bmQgSW5mb3JtYXRpazEOMAwGA1UEBAwFV2llc2UxDzANBgNVBCoM +Bk9saXZlcjEVMBMGA1UEAwwMT2xpdmVyIFdpZXNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA1tXu5MsVxQ8Cn9RLiJPo74E/DwQaGk7jMODB4GyHMgdocL99RiWJkzv6edL1gHEGvVL/ +NWXP2CPgjD3ckpQJCbGIqtRRpOPkenxQzDzYmDn14YIbBDvWmCUNGEYr/YVkz8OcRYM12R7KnJ56 +YYQUZY/0KPd+C9wY79EsTRA+Gl9PYXp2yfBsA3d2IJxbmdP5mLU9GoJ/q8jqnIGk9IpPZ5sLfLbd +sMO7EbXblMMUpkDCLvau7/nTuVsf66VrxLwR5i9lMGoetK3uPQUIA4SDMvEnJUm1o+VSr+IxtIZy +bG5gO99uJ+6xxXcw8rQB1ejvTOrjf9LK8yD6V80lN9qI9wIDAQABo4ICijCCAoYwPgYDVR0gBDcw +NTAPBg0rBgEEAYGtIYIsAQEEMBAGDisGAQQBga0hgiwBAQQIMBAGDisGAQQBga0hgiwCAQQIMAkG +A1UdEwQCMAAwDgYDVR0PAQH/BAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAd +BgNVHQ4EFgQUpC8bD5xOFMZIDBQF1RAv1QHZ+iswHwYDVR0jBBgwFoAUazqYi/nyU4na4K2yMh4J +H+iqO3QwXAYDVR0RBFUwU4EZb2xpdmVyLndpZXNlQGZ1LWJlcmxpbi5kZYEZT2xpdmVyLldpZXNl +QGZ1LWJlcmxpbi5kZYEbd2llc2VvbGlAemVkYXQuZnUtYmVybGluLmRlMIGNBgNVHR8EgYUwgYIw +P6A9oDuGOWh0dHA6Ly9jZHAxLnBjYS5kZm4uZGUvZGZuLWNhLWdsb2JhbC1nMi9wdWIvY3JsL2Nh +Y3JsLmNybDA/oD2gO4Y5aHR0cDovL2NkcDIucGNhLmRmbi5kZS9kZm4tY2EtZ2xvYmFsLWcyL3B1 +Yi9jcmwvY2FjcmwuY3JsMIHbBggrBgEFBQcBAQSBzjCByzAzBggrBgEFBQcwAYYnaHR0cDovL29j +c3AucGNhLmRmbi5kZS9PQ1NQLVNlcnZlci9PQ1NQMEkGCCsGAQUFBzAChj1odHRwOi8vY2RwMS5w +Y2EuZGZuLmRlL2Rmbi1jYS1nbG9iYWwtZzIvcHViL2NhY2VydC9jYWNlcnQuY3J0MEkGCCsGAQUF +BzAChj1odHRwOi8vY2RwMi5wY2EuZGZuLmRlL2Rmbi1jYS1nbG9iYWwtZzIvcHViL2NhY2VydC9j +YWNlcnQuY3J0MA0GCSqGSIb3DQEBCwUAA4IBAQBXTTIaa3C3miMEok7yGMkUyB16o0ctMzVhRexg +rARlBi4uYprn1URJGLRVmVzXb1H7DbSYEjJtCr6lJITQP0pcwQC1y47ObWqAVKNx+hDM5VsWwSB+ +xXlGjIuKb8ZLxCcTVIsvlSZw/i8XU4xfUB7GmgNtFK2lzAtPT8LlG0i3ph4Zshj7wzMJpxxbCgdG +IMGLtKIimmegpzIB4N0jecm4C5DcaYf1V874Fw5C5FxG8yvQLlGhbHGsVXqBeYyRzv0UY/kLLnHr +FO57Tq24uPZuDOg06nIQb/SUZ8qXwz0sqBt0j4NP6qqj/Ur7y81a8YRQaKFantJFh2jQu6uz5kOW +MYIDnTCCA5kCAQEwgZ4wgY0xCzAJBgNVBAYTAkRFMUUwQwYDVQQKDDxWZXJlaW4genVyIEZvZXJk +ZXJ1bmcgZWluZXMgRGV1dHNjaGVuIEZvcnNjaHVuZ3NuZXR6ZXMgZS4gVi4xEDAOBgNVBAsMB0RG +Ti1QS0kxJTAjBgNVBAMMHERGTi1WZXJlaW4gR2xvYmFsIElzc3VpbmcgQ0ECDCRYa5dfehx1JdhM +tDANBglghkgBZQMEAgEFAKCCAc8wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0B +CQUxDxcNMjEwMzEwMTYwNDI3WjAvBgkqhkiG9w0BCQQxIgQgQK/YjIICeu1uq9WnnrVErVKTkvSR +vHPv/Kuk53C9A1Ewga8GCSsGAQQBgjcQBDGBoTCBnjCBjTELMAkGA1UEBhMCREUxRTBDBgNVBAoM +PFZlcmVpbiB6dXIgRm9lcmRlcnVuZyBlaW5lcyBEZXV0c2NoZW4gRm9yc2NodW5nc25ldHplcyBl +LiBWLjEQMA4GA1UECwwHREZOLVBLSTElMCMGA1UEAwwcREZOLVZlcmVpbiBHbG9iYWwgSXNzdWlu +ZyBDQQIMJFhrl196HHUl2Ey0MIGxBgsqhkiG9w0BCRACCzGBoaCBnjCBjTELMAkGA1UEBhMCREUx RTBDBgNVBAoMPFZlcmVpbiB6dXIgRm9lcmRlcnVuZyBlaW5lcyBEZXV0c2NoZW4gRm9yc2NodW5n c25ldHplcyBlLiBWLjEQMA4GA1UECwwHREZOLVBLSTElMCMGA1UEAwwcREZOLVZlcmVpbiBHbG9i -YWwgSXNzdWluZyBDQQIMHquxIGDgCGtc8ZZZMA0GCWCGSAFlAwQCAQUAoIIBzzAYBgkqhkiG9w0B -CQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMTAyMTYxNjE2NThaMC8GCSqGSIb3DQEJ -BDEiBCC8Fytiv1ZpmEb8bvmg27rVyebYhhu4n7CvuB65krlt7DCBrwYJKwYBBAGCNxAEMYGhMIGe -MIGNMQswCQYDVQQGEwJERTFFMEMGA1UECgw8VmVyZWluIHp1ciBGb2VyZGVydW5nIGVpbmVzIERl -dXRzY2hlbiBGb3JzY2h1bmdzbmV0emVzIGUuIFYuMRAwDgYDVQQLDAdERk4tUEtJMSUwIwYDVQQD -DBxERk4tVmVyZWluIEdsb2JhbCBJc3N1aW5nIENBAgweq7EgYOAIa1zxllkwgbEGCyqGSIb3DQEJ -EAILMYGhoIGeMIGNMQswCQYDVQQGEwJERTFFMEMGA1UECgw8VmVyZWluIHp1ciBGb2VyZGVydW5n -IGVpbmVzIERldXRzY2hlbiBGb3JzY2h1bmdzbmV0emVzIGUuIFYuMRAwDgYDVQQLDAdERk4tUEtJ -MSUwIwYDVQQDDBxERk4tVmVyZWluIEdsb2JhbCBJc3N1aW5nIENBAgweq7EgYOAIa1zxllkwDQYJ -KoZIhvcNAQEBBQAEggEAMT5sbk0eX0B6kJjLa22vWULGUjKIo/klAOLzeTbejIdGy+kRxZP3Ztxy -cROX8JxHGMPslJ9lmnbmIYwCqb7JgIZjyelJNDESzPzo12krkhH0t8J3tUFC/TyKCQg1JBRNdT+L -08/7/xYJL7DOG1CXTiIQi7P29QpCGCisfuEKJ8ac3cZrT81EIfGnEoIAKam+RFlE4TDAZEBF8qgm -s6pxT8aSUjMnMP8ZtTHQpNbUsMqLkilXYsnSREleAbqptItWbJ+qsGJMcC4wc28tNGOLEsdYjGsH -eM+AIln6HACsMCpqXSkxgkc7T7F7DTlb+FwGe+UyaZOj1O3LzoS+b8jijAAAAAAAAA== ---Apple-Mail=_58C9D0E1-2B0A-4882-8F02-411E8FCB5BF0-- +YWwgSXNzdWluZyBDQQIMJFhrl196HHUl2Ey0MA0GCSqGSIb3DQEBAQUABIIBABR/msBlq4aqrh2W +2hbofRVT4XMRl7Brw9XZa2+9ZqNOwUemCpvqk61frnJE5adoyuCuX3v9633kmJZyenxVQr5Vfvlb +QL1lR+0yFqPWFoHqo34oVF/9CgMNxLm1iP6DjBx96ugWKGRcrQ+cXSdwKsiWbjE4SXbpdmNhisLi +FI7DVK0BSGkWk+3YuXdqGPfe77o/s1uLMjhldJuEgPC4hWqyvPsOKtUspNG4Imj/80Wnj+DJI+gW +uZaaBgHTsXJ31NYyNoVKCTuvzDTRdxm5+MY/ehoP1FNexjOhhUFYvvYvpAh4mlI6kI/vcaXWYtTn +i8rsvKhpg6/l/2dKGDOmQpgAAAAAAAA= +--Apple-Mail=_9BE88FFD-313B-4C05-B770-19F608BDA482--