diff --git a/README.md b/README.md index 54dcbc175594c4656fa66d449c06aab9c4987dec..7e9bd4e6c5af89890b60f16bf0f02f3c1476095d 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,8 @@ Please try to adhere to the [Github Swift Style Guide](https://github.com/github (If you want it super specific, there is also the very deep [Ray Wenderlich Guide](https://github.com/raywenderlich/swift-style-guide)) -You can use the [Swimat Plugin](https://github.com/Jintin/Swimat) to do parts of that automatically. \ No newline at end of file +You can use the [Swimat Plugin](https://github.com/Jintin/Swimat) to do parts of that automatically. + +## Documentation + +Please document your code using [markup](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html#//apple_ref/doc/uid/TP40016497-CH2-SW1) \ No newline at end of file diff --git a/enzevalos_iphone.xcodeproj/project.pbxproj b/enzevalos_iphone.xcodeproj/project.pbxproj index a3d1c04c475d86fc4018bca08208f067377bc056..691e4ed123c0307f1ee48248a9ec9c4bddddd4ae 100644 --- a/enzevalos_iphone.xcodeproj/project.pbxproj +++ b/enzevalos_iphone.xcodeproj/project.pbxproj @@ -171,6 +171,7 @@ 47F867E22052B48E00AA832F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 47F867E12052B48E00AA832F /* libz.tbd */; }; 47F867E42052B49800AA832F /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 47F867E32052B49800AA832F /* libbz2.tbd */; }; 50F2E7D66366C779705987A7 /* Pods_enzevalos_iphoneUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF67EF30BB065CC9C0D17940 /* Pods_enzevalos_iphoneUITests.framework */; }; + 71DF08982421520D00162B74 /* EmailStringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DF08972421520D00162B74 /* EmailStringExtensionTests.swift */; }; 71DFE5BA240679E80042019C /* HeaderExtractionValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DFE5B9240679E80042019C /* HeaderExtractionValues.swift */; }; 7500EE9D4F3130671F5C1AE2 /* Pods_enzevalos_iphoneTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7977EA7012D8E98D186D5C60 /* Pods_enzevalos_iphoneTests.framework */; }; 8428A8531F4369C0007649A5 /* Gamification.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8428A8521F4369C0007649A5 /* Gamification.storyboard */; }; @@ -190,7 +191,7 @@ 8428A8711F436A1E007649A5 /* GamificationStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8428A86D1F436A1E007649A5 /* GamificationStatusViewController.swift */; }; 8428A8831F436AC9007649A5 /* GamificationDataUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8428A8561F4369EA007649A5 /* GamificationDataUnitTest.swift */; }; 8428A8841F436ACC007649A5 /* GamificationElements.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8428A8541F4369CF007649A5 /* GamificationElements.xcassets */; }; - 988C9C5D240D507A006213F0 /* PhishingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 988C9C5C240D507A006213F0 /* PhishingTests.swift */; }; + 988C9C5D240D507A006213F0 /* UrlStringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 988C9C5C240D507A006213F0 /* UrlStringExtensionTests.swift */; }; A102AA8A1EDDB4F40024B457 /* videoOnboarding2.m4v in Resources */ = {isa = PBXBuildFile; fileRef = A102AA891EDDB4E80024B457 /* videoOnboarding2.m4v */; }; A1083A541E8BFEA6003666B7 /* Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1083A531E8BFEA6003666B7 /* Onboarding.swift */; }; A10DAA5721F37600005D8BBB /* IntroInfoButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A10DAA5621F37600005D8BBB /* IntroInfoButton.swift */; }; @@ -589,7 +590,9 @@ 47F867E32052B49800AA832F /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; }; 48C250BB32BF11B683003BA1 /* Pods-enzevalos_iphone-enzevalos_iphoneUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphone-enzevalos_iphoneUITests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphone-enzevalos_iphoneUITests/Pods-enzevalos_iphone-enzevalos_iphoneUITests.debug.xcconfig"; sourceTree = "<group>"; }; 66E758F271CD65AB3E5FE7A7 /* Pods-enzevalos_iphoneUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphoneUITests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphoneUITests/Pods-enzevalos_iphoneUITests.debug.xcconfig"; sourceTree = "<group>"; }; + 670159DF240FB4E800797FA5 /* enzevalos_iphone 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "enzevalos_iphone 9.xcdatamodel"; sourceTree = "<group>"; }; 6EBCCD02AD3B95D8317810E2 /* Pods-enzevalos_iphoneTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphoneTests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphoneTests/Pods-enzevalos_iphoneTests.debug.xcconfig"; sourceTree = "<group>"; }; + 71DF08972421520D00162B74 /* EmailStringExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailStringExtensionTests.swift; sourceTree = "<group>"; }; 71DFE5B9240679E80042019C /* HeaderExtractionValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderExtractionValues.swift; sourceTree = "<group>"; }; 796D16D79BED5D60B580E602 /* Pods-enzevalos_iphoneUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphoneUITests.release.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphoneUITests/Pods-enzevalos_iphoneUITests.release.xcconfig"; sourceTree = "<group>"; }; 7977EA7012D8E98D186D5C60 /* Pods_enzevalos_iphoneTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_enzevalos_iphoneTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -617,7 +620,7 @@ 97AACD2324178C230078A68E /* AuthenticationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationModel.swift; sourceTree = "<group>"; }; 97C5279E241A9F7B0030BBC9 /* AuthenticationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationTests.swift; sourceTree = "<group>"; }; 97C527A0241AA4090030BBC9 /* GeneratedMocks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GeneratedMocks.swift; path = enzevalos_iphoneTests/GeneratedMocks.swift; sourceTree = SOURCE_ROOT; }; - 988C9C5C240D507A006213F0 /* PhishingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhishingTests.swift; sourceTree = "<group>"; }; + 988C9C5C240D507A006213F0 /* UrlStringExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlStringExtensionTests.swift; sourceTree = "<group>"; }; 9A132EDE8BCA06ACDB505C22 /* Pods-enzevalos_iphoneUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphoneUITests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphoneUITests/Pods-enzevalos_iphoneUITests.debug.xcconfig"; sourceTree = "<group>"; }; 9B3D62838C729BAC6832270A /* Pods-enzevalos_iphone-AdHoc.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphone-AdHoc.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphone-AdHoc/Pods-enzevalos_iphone-AdHoc.debug.xcconfig"; sourceTree = "<group>"; }; A102AA891EDDB4E80024B457 /* videoOnboarding2.m4v */ = {isa = PBXFileReference; lastKnownFileType = file; path = videoOnboarding2.m4v; sourceTree = "<group>"; }; @@ -1217,6 +1220,15 @@ path = private; sourceTree = "<group>"; }; + 71DF0896242151E200162B74 /* phishing */ = { + isa = PBXGroup; + children = ( + 988C9C5C240D507A006213F0 /* UrlStringExtensionTests.swift */, + 71DF08972421520D00162B74 /* EmailStringExtensionTests.swift */, + ); + path = phishing; + sourceTree = "<group>"; + }; 78280F99990BFF65543B7F0B /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1407,8 +1419,8 @@ isa = PBXGroup; children = ( 0ED9072F24338E3C008CF9D0 /* SMIMETests.swift */, + 71DF0896242151E200162B74 /* phishing */, 97C5279D241A9F690030BBC9 /* authentication */, - 988C9C5C240D507A006213F0 /* PhishingTests.swift */, 47F0376C22A7278A0005C9DE /* private */, 470709202189C24800DF71A3 /* testKeys */, 470709112189BB4A00DF71A3 /* testMails */, @@ -1712,6 +1724,7 @@ A13526881D955BE000D3BFE1 /* Sources */, A13526891D955BE000D3BFE1 /* Frameworks */, A135268A1D955BE000D3BFE1 /* Resources */, + 0811D368D52F1D9B243500D4 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1895,6 +1908,24 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 0811D368D52F1D9B243500D4 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-enzevalos_iphoneTests/Pods-enzevalos_iphoneTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Cuckoo/Cuckoo.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cuckoo.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-enzevalos_iphoneTests/Pods-enzevalos_iphoneTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3992B0CB6412E8526773B814 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -2206,13 +2237,14 @@ buildActionMask = 2147483647; files = ( 8428A8831F436AC9007649A5 /* GamificationDataUnitTest.swift in Sources */, + 71DF08982421520D00162B74 /* EmailStringExtensionTests.swift in Sources */, 3EC35F302003838E008BDF95 /* InvitationTests.swift in Sources */, 474054982244D7A9007CF83B /* MailServerConfigurationTest.swift in Sources */, 479B5977206914BE00B3944D /* CryptoTests.swift in Sources */, A15D215F223BE6E4003E0CE0 /* MailTest.swift in Sources */, 0ED9073024338E3C008CF9D0 /* SMIMETests.swift in Sources */, - 47EABF0F2420C63600774A93 /* AuthenticationTests.swift in Sources */, - 988C9C5D240D507A006213F0 /* PhishingTests.swift in Sources */, + 47EABF0F2420C63600774A93 /* AuthenticationTests.swift in Sources */, + 988C9C5D240D507A006213F0 /* UrlStringExtensionTests.swift in Sources */, 4715F637202A0248001BFFD0 /* CoreDataTests.swift in Sources */, 47C22281218AFD6300BD2C2B /* AutocryptTest.swift in Sources */, 478154AE2200641900A931EC /* StudyTest.swift in Sources */, @@ -2754,6 +2786,7 @@ A135267F1D955BDF00D3BFE1 /* enzevalos_iphone.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + 670159DF240FB4E800797FA5 /* enzevalos_iphone 9.xcdatamodel */, 4751C6FD2344D169006B2A4D /* enzevalos_iphone 8.xcdatamodel */, 4751C6F0233CE1E4006B2A4D /* enzevalos_iphone 7.xcdatamodel */, A1909719220F887D00FA7B2A /* enzevalos_iphone 6.xcdatamodel */, @@ -2763,7 +2796,7 @@ 47B2318A1F0D458100961B28 /* enzevalos_iphone 2.xcdatamodel */, A13526801D955BDF00D3BFE1 /* enzevalos_iphone.xcdatamodel */, ); - currentVersion = 4751C6FD2344D169006B2A4D /* enzevalos_iphone 8.xcdatamodel */; + currentVersion = 670159DF240FB4E800797FA5 /* enzevalos_iphone 9.xcdatamodel */; path = enzevalos_iphone.xcdatamodeld; sourceTree = "<group>"; versionGroupType = wrapper.xcdatamodel; diff --git a/enzevalos_iphone/DataHandler.swift b/enzevalos_iphone/DataHandler.swift index c0556cbda925d11c84f388d911eca264e2f30cc7..402f99c00c383dced15b6323e628c6a4c7ec60c5 100644 --- a/enzevalos_iphone/DataHandler.swift +++ b/enzevalos_iphone/DataHandler.swift @@ -297,6 +297,20 @@ class DataHandler { } } + func deleteRelatedSecretKeys() { + let keys = findSecretKeys() + let pgp = SwiftPGP() + pgp.deleteSecretKeys() + for key in keys { + if let mails = key.relatedDecryptedMails as? Set<PersistentMail> { + for mail in mails { + mail.relatedSecrectKey = nil + } + } + delete(key: key) + } + } + func deleteMail(with uid: UInt64) { self.deleteNum("PersistentMail", type: "uid", search: uid) } @@ -866,7 +880,7 @@ class DataHandler { } // -------- End handle to, cc, from addresses -------- - func createMail(_ uid: UInt64, sender: MCOAddress?, receivers: [MCOAddress], cc: [MCOAddress], time: Date, received: Bool, subject: String, body: String?, readableAttachments: Set<TempAttachment> = Set<TempAttachment>(), flags: MCOMessageFlag, record: KeyRecord?, autocrypt: Autocrypt?, decryptedData: CryptoObject?, folderPath: String, secretKey: String?, references: [String] = [], mailagent: String? = nil, messageID: String? = nil, encryptedBody: String?, storeEncrypted: Bool = false) -> PersistentMail? { + func createMail(_ uid: UInt64, sender: MCOAddress?, receivers: [MCOAddress], cc: [MCOAddress], time: Date, received: Bool, subject: String, body: String?, readableAttachments: Set<TempAttachment> = Set<TempAttachment>(), flags: MCOMessageFlag, record: KeyRecord?, autocrypt: Autocrypt?, decryptedData: CryptoObject?, folderPath: String, secretKey: String?, references: [String] = [], mailagent: String? = nil, messageID: String? = nil, encryptedBody: String?, storeEncrypted: Bool = false, isKeyImported: Bool = true, attachedSignature: Data? = nil) -> PersistentMail? { guard let sender = sender else { return nil @@ -897,6 +911,7 @@ class DataHandler { } else { mail.body = nil } + // relatedSecretKey mail.secretKey = secretKey mail.folder = myfolder @@ -915,7 +930,7 @@ class DataHandler { mail.unableToDecrypt = false mail.storeEncrypted = storeEncrypted mail.received = received - + mail.attachedSignature = attachedSignature handleFromAddress(sender, fromMail: mail, autocrypt: autocrypt) diff --git a/enzevalos_iphone/OutgoingMail.swift b/enzevalos_iphone/OutgoingMail.swift index 797b0405079748539729f290c501a50d441b889f..52904e4394bb752a37498454792a2f42634242e9 100644 --- a/enzevalos_iphone/OutgoingMail.swift +++ b/enzevalos_iphone/OutgoingMail.swift @@ -46,6 +46,7 @@ class OutgoingMail { private let textContent: String? private var htmlContent: String? = nil private var attachments: [MCOAttachment] = [] + private var attachedSignature: Data? var pgpData: Data? { get { return createEncData() @@ -115,6 +116,13 @@ class OutgoingMail { self.sendEncryptedIfPossible = sendEncryptedIfPossible self.attachments = attachments self.orderReceivers() + if let attachment = attachments.first { + let signature = OutgoingMail.extractPGPSignature(attachment: attachment) + if let signatureData = signature.first?.data(using: .utf8) { + self.attachedSignature = signatureData + } + } + } init(mail: PersistentMail){ @@ -129,6 +137,7 @@ class OutgoingMail { self.orderReceivers() self.mail = mail self.loggingMail = mail.isCorrectlySigned //TODO FIX HERE + self.attachedSignature = mail.attachedSignature } func store() -> PersistentMail? { @@ -137,6 +146,7 @@ class OutgoingMail { mail?.decryptedBody = htmlContent mail?.isEncrypted = sendEncryptedIfPossible mail?.isCorrectlySigned = self.loggingMail + mail?.attachedSignature = self.attachedSignature self.mail = mail return mail } @@ -246,7 +256,15 @@ class OutgoingMail { return true } - + private static func extractPGPSignature(attachment: MCOAttachment) -> [String] { + if let content = attachment.decodedString() { + return PGPPart.SIGNATURE.findPGPPartInString(content: content) + } + else if let content = String(data: attachment.data, encoding: .ascii){ + return PGPPart.SIGNATURE.findPGPPartInString(content: content) + } + return [] + } private func createPlainData() -> Data { let builder = createBuilder() diff --git a/enzevalos_iphone/PersistentMail +CoreDataProperties.swift b/enzevalos_iphone/PersistentMail +CoreDataProperties.swift index e032c064af288dbb73d4f397fabe1a365f2f8056..16d1b3fb2efb0788c1eabe2de582d9e0cfbd5281 100644 --- a/enzevalos_iphone/PersistentMail +CoreDataProperties.swift +++ b/enzevalos_iphone/PersistentMail +CoreDataProperties.swift @@ -23,7 +23,7 @@ import CoreData extension PersistentMail { - @nonobjc open override class func fetchRequest() -> NSFetchRequest<NSFetchRequestResult> { + @nonobjc public class func fetchRequest() -> NSFetchRequest<PersistentMail> { return NSFetchRequest(entityName: "PersistentMail"); } @@ -79,10 +79,20 @@ extension PersistentMail { } } + /** + Default value: True (in case no key was in mail is provided) + True: Provided key in mail body is imported or discareded. + False: No actions have be done yet + */ + @NSManaged public var isKeyImported: Bool /** Please use encryption state */ @NSManaged public var isEncrypted: Bool + /** + Some Mail have the signature attached to them separately + */ + @NSManaged public var attachedSignature: Data? /** This mail is signed but check isSigned, too. We recommand to use signature state @@ -149,7 +159,10 @@ extension PersistentMail { Only for traveler scenario. */ @NSManaged public var decryptedKey: PersistentKey? - + /** + Related secrect Key for mail. + */ + @NSManaged public var relatedSecrectKey: SecretKey? /** Sender mail client. */ diff --git a/enzevalos_iphone/SecretKey+CoreDataProperties.swift b/enzevalos_iphone/SecretKey+CoreDataProperties.swift index 47d468117e3f0405a370242a77bf88c526e708ac..9c3470fad3931cbd50fc232aec613e3d2ec1e7c4 100644 --- a/enzevalos_iphone/SecretKey+CoreDataProperties.swift +++ b/enzevalos_iphone/SecretKey+CoreDataProperties.swift @@ -44,6 +44,10 @@ extension SecretKey { A Set of mails where as the key was used for decryption. */ @NSManaged public var decryptedMails: NSSet? + /** + A Set of mails where the relatedSecrectKey was used for decryption. + */ + @NSManaged public var relatedDecryptedMails: NSSet? } diff --git a/enzevalos_iphone/StringExtension.swift b/enzevalos_iphone/StringExtension.swift index 4b6f0dbe49224fccfc31548bf59880a42b70ce7f..a9656f4fd454fdb3c13cc00530b489bda328b586 100644 --- a/enzevalos_iphone/StringExtension.swift +++ b/enzevalos_iphone/StringExtension.swift @@ -20,10 +20,10 @@ import Foundation extension String { - + static func random(length: Int = 20) -> String { var randomBytes = Data(count: length) - + let result = randomBytes.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, length, $0) } @@ -39,7 +39,7 @@ extension String { func trimmed() -> String { return self.trimmingCharacters(in: .whitespacesAndNewlines) } - + func removeNewLines() -> String { let components = self.components(separatedBy: .whitespacesAndNewlines) return components.filter { !$0.isEmpty }.joined(separator: " ") @@ -72,13 +72,13 @@ extension String { let splittetMailArr = [mailLocalIdentity] + subDomainArr return splittetMailArr } - + func getLocalMailIdentity() -> String{ guard let localIdentity = self.splitAddress().first else { return ""} return localIdentity } - + func getSubdomains() -> [String]{ if self == ""{ return [] @@ -88,30 +88,58 @@ extension String { subDomains.removeLast() return subDomains } - - - func getDomain() -> String{ + + + func getDomain() -> String{ guard let domain = self.splitAddress().last else {return ""} return domain - } + } } extension String { - func findMailAddress() -> [String]{ - let splitString = self.split(separator: " ").map(String.init) - var results:[String] = [] - for val in splitString { - if val.contains("@"){ - results.append(val) - } + func isValidEmail() -> Bool { + + let emailRegEx = "(?!\\u10000-\\uEFFFF.*\\.\\.)([^@\\s]{2,64}+@[^@\\s]+\\.[^@\\s\\.]+$)" + let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx) + return emailTest.evaluate(with: self) + } + + func findEmails() -> [String]{ + let pattern = "[\\u10000-\\uEFFFF.!#$%&'*+/=?^_`{|}~-]+@[\\u10000-\\uEFFFF.!#$%&'*+/=?^_`{|}~-]+\\.[\\u10000-\\uEFFFF.!#$%&'*+/=?^_`{|}~-]{2,64}" //\\U00010000-\\U0010FFFF + let range = NSRange(location: 0, length: self.utf16.count) + let regex = try! NSRegularExpression(pattern: pattern) + let check = regex.matches(in: self, options: [], range: range) + var foundLinks:[String] = [] + for match in check{ + guard let range = Range(match.range, in: self) else {continue} + let result = String(self[range]) + if result.isValidEmail(){ + foundLinks.append(result) } - return(results) + } + return foundLinks } - + + func url2ip() -> String? { + let host = CFHostCreateWithName(nil,self as CFString).takeRetainedValue() + CFHostStartInfoResolution(host, .addresses, nil) + var success: DarwinBoolean = false + if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray?, + let theAddress = addresses.firstObject as? NSData { + var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) + if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length), + &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 { + let numAddress = String(cString: hostname) + return(numAddress) + } + } + return nil + } + //for text only, not for hyperlink detection - func findURL() -> [String?]{ - var urls : [String?] = [] + func findURL() -> [String]{ + var urls : [String] = [] do { let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) detector.enumerateMatches(in: self, options: [], range: NSMakeRange(0, self.count), using: { (result, _, _) in @@ -140,7 +168,7 @@ extension String { } return (foundLinks) } - + func htmlTagURL() -> [String]{ let htmlTagUrlPattern = "(?<=href=')(.*?)(?='>)|(?<=href=\")(.*?)(?=\">)" let range = NSRange(location: 0, length: self.utf16.count) @@ -154,7 +182,7 @@ extension String { return (foundLinks) } - + func htmlTagLinkName() -> [String]{ let htmlTagLinkNamePattern = "(?<='>)(.*?)(?=<\\/a>)|(?<=\">)(.*?)(?=<)" let range = NSRange(location: 0, length: self.utf16.count) diff --git a/enzevalos_iphone/SwiftPGP.swift b/enzevalos_iphone/SwiftPGP.swift index 9cdcd750ac49117f9bc3f1fd2258589944beaa1c..79d04e45079728f2e887ea0f55c352a10f008b37 100644 --- a/enzevalos_iphone/SwiftPGP.swift +++ b/enzevalos_iphone/SwiftPGP.swift @@ -666,7 +666,7 @@ class SwiftPGP: Encryption { return } // Try to verify mail signature - sigState = verifySignature(data: data, attachedSignature: nil, keys: key) + sigState = verifySignature(data: data, attachedSignature: mail.attachedSignature, keys: key) if sigState == SignatureState.ValidSignature { // Update database mail.isSigned = true diff --git a/enzevalos_iphone/SwiftUI/Inbox/Inbox.swift b/enzevalos_iphone/SwiftUI/Inbox/Inbox.swift index 9a35defc83480fc689c282dd69598ce047646c94..97a2a50f6e44555faac8b7a1649feeabbb4e43ed 100644 --- a/enzevalos_iphone/SwiftUI/Inbox/Inbox.swift +++ b/enzevalos_iphone/SwiftUI/Inbox/Inbox.swift @@ -45,7 +45,7 @@ struct Inbox: View { private var mailList: some View { List (self.keyrecords.filter(filterKeyRecord), id: \.self){ record in - KeyRecordRow(keyrecord: record, coord: self.coord) + KeyRecordRow(keyrecord: record, coord: self.coord).environment(\.managedObjectContext, self.managedObjectContext) } .resignKeyboardOnDragGesture() // hide keyboard when dragging } diff --git a/enzevalos_iphone/SwiftUI/Inbox/InboxCoordinator.swift b/enzevalos_iphone/SwiftUI/Inbox/InboxCoordinator.swift index af075479c2b46e6c7926f5ad273bb616d076dd84..f1e3b56bfc5c6d0742427075165c93ac4b838f91 100644 --- a/enzevalos_iphone/SwiftUI/Inbox/InboxCoordinator.swift +++ b/enzevalos_iphone/SwiftUI/Inbox/InboxCoordinator.swift @@ -40,7 +40,7 @@ class InboxCoordinator { } else { let context = DataHandler.handler.managedObjectContext - let vc = UIHostingController(rootView: Inbox(coord: self).environment(\.managedObjectContext,context)) + let vc = UIHostingController(rootView: Inbox(coord: self).environment(\.managedObjectContext, context)) inbox = vc root.pushViewController(vc, animated: true) } diff --git a/enzevalos_iphone/SwiftUI/Inbox/KeyRecordRow.swift b/enzevalos_iphone/SwiftUI/Inbox/KeyRecordRow.swift index e8bc192f461adb52329a9eb7ff5b17881abe97ae..4c85251804dddf30d59c5173a2369d8ab0e06bf5 100644 --- a/enzevalos_iphone/SwiftUI/Inbox/KeyRecordRow.swift +++ b/enzevalos_iphone/SwiftUI/Inbox/KeyRecordRow.swift @@ -9,9 +9,27 @@ import SwiftUI struct KeyRecordRow: View { - @ObservedObject var keyrecord: KeyRecord + @Environment(\.managedObjectContext) var managedObjectContext + + var keyrecord: KeyRecord let coord: InboxCoordinator + var fetchRequest: FetchRequest<PersistentMail> + var mails: FetchedResults<PersistentMail> { + fetchRequest.wrappedValue + } + + init(keyrecord: KeyRecord, coord: InboxCoordinator) { + self.keyrecord = keyrecord + self.coord = coord + let pred = NSPredicate(format: "record == %@", keyrecord) + let folderPredicate = NSPredicate(format: "folder == %@", Folder.inbox) + let predicates = NSCompoundPredicate(andPredicateWithSubpredicates: [pred, folderPredicate]) + let sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)] + fetchRequest = FetchRequest<PersistentMail>(entity: PersistentMail.entity(), sortDescriptors: sortDescriptors, predicate: predicates, animation: nil) + } + + var body: some View { VStack { HStack(alignment: .lastTextBaseline) { @@ -28,6 +46,13 @@ struct KeyRecordRow: View { } } + var secondMail: PersistentMail?{ + if mails.count > 1 { + return mails[2] + } + return nil + } + /* Displays the first (and second) mail */ @@ -35,11 +60,11 @@ struct KeyRecordRow: View { let spacing = CGFloat(10) let offset = -(spacing / 2) - if let first = self.keyrecord.firstMail { - if let second = self.keyrecord.secondMail { + if let first = mails.first { + if mails.count > 1 { return AnyView(VStack(alignment: .leading, spacing: spacing) { MailView(mail: first, coord: coord) - MailView(mail: second, coord: coord) + MailView(mail: mails[1], coord: coord) .background(Stroke(offsetY: offset)) }) } else { @@ -85,6 +110,7 @@ struct KeyRecordRow: View { extension KeyRecord { + public var firstMail: PersistentMail? { get { return mails.first diff --git a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/.xccurrentversion b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/.xccurrentversion index b24719e7c69bc07e985d15759e78b50ea73a1a2e..8723d24cbe7999b90840d81bec8c2e4809b58ee6 100644 --- a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/.xccurrentversion +++ b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ <plist version="1.0"> <dict> <key>_XCCurrentVersionName</key> - <string>enzevalos_iphone 8.xcdatamodel</string> + <string>enzevalos_iphone 9.xcdatamodel</string> </dict> </plist> diff --git a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 9.xcdatamodel/contents b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 9.xcdatamodel/contents new file mode 100644 index 0000000000000000000000000000000000000000..a537bc2263e93c6d9eedf4ba3c0983aa767712d7 --- /dev/null +++ b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 9.xcdatamodel/contents @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="15702" systemVersion="19D76" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> + <entity name="Account" representedClassName="Account" syncable="YES" codeGenerationType="class"> + <attribute name="archiveFolderPath" attributeType="String"/> + <attribute name="displayName" attributeType="String"/> + <attribute name="draftFolderPath" attributeType="String"/> + <attribute name="inboxFolderPath" attributeType="String"/> + <attribute name="loginName" attributeType="String"/> + <attribute name="prefEnc" optional="YES" attributeType="String"/> + <attribute name="prefMailAdr" optional="YES" attributeType="String"/> + <attribute name="prefSecretKeyID" optional="YES" attributeType="String"/> + <attribute name="sentFolderPath" attributeType="String"/> + <attribute name="trashFolderPath" attributeType="String"/> + <relationship name="aliase" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="account" inverseEntity="Mail_Address"/> + <relationship name="imap" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Server" inverseName="imap" inverseEntity="Server"/> + <relationship name="keys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="SecretKey" inverseName="account" inverseEntity="SecretKey"/> + <relationship name="smtp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Server" inverseName="smtp" inverseEntity="Server"/> + </entity> + <entity name="Attachment" representedClassName="Attachment" syncable="YES" codeGenerationType="class"> + <attribute name="contentID" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="data" attributeType="Binary"/> + <attribute name="encryptionState" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="isExplicitAttachment" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="mcoPartType" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="mimeType" attributeType="String"/> + <attribute name="name" attributeType="String"/> + <attribute name="signatureState" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <relationship name="mail" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="attachments" inverseEntity="PersistentMail"/> + <relationship name="parentOf" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Attachment" inverseName="partOf" inverseEntity="Attachment"/> + <relationship name="partOf" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Attachment" inverseName="parentOf" inverseEntity="Attachment"/> + </entity> + <entity name="EnzevalosContact" representedClassName="EnzevalosContact" syncable="YES"> + <attribute name="cnidentifier" optional="YES" attributeType="String"/> + <attribute name="color" optional="YES" attributeType="Transformable" customClassName="UIColor"/> + <attribute name="displayname" attributeType="String"/> + <relationship name="addresses" toMany="YES" deletionRule="Cascade" destinationEntity="Mail_Address" inverseName="contact" inverseEntity="Mail_Address"/> + <relationship name="keyrecords" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="KeyRecord" inverseName="contact" inverseEntity="KeyRecord"/> + </entity> + <entity name="Folder" representedClassName="Folder" syncable="YES"> + <attribute name="delimiter" optional="YES" attributeType="String"/> + <attribute name="flags" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="icon" optional="YES" attributeType="String"/> + <attribute name="lastUpdate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="maxID" optional="YES" attributeType="Decimal" defaultValueString="1"/> + <attribute name="minUID" optional="YES" attributeType="Decimal" defaultValueString="0.0"/> + <attribute name="path" attributeType="String"/> + <attribute name="pseudonym" attributeType="String"/> + <attribute name="uidvalidity" optional="YES" attributeType="Decimal" defaultValueString="0.0"/> + <relationship name="keyRecords" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="KeyRecord" inverseName="folder" inverseEntity="KeyRecord"/> + <relationship name="mails" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PersistentMail" inverseName="folder" inverseEntity="PersistentMail"/> + <relationship name="parent" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Folder" inverseName="subfolder" inverseEntity="Folder"/> + <relationship name="subfolder" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Folder" inverseName="parent" inverseEntity="Folder"/> + </entity> + <entity name="KeyRecord" representedClassName="KeyRecord" syncable="YES"> + <attribute name="newestDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="nick" optional="YES" attributeType="String"/> + <relationship name="contact" maxCount="1" deletionRule="Nullify" destinationEntity="EnzevalosContact" inverseName="keyrecords" inverseEntity="EnzevalosContact"/> + <relationship name="folder" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Folder" inverseName="keyRecords" inverseEntity="Folder"/> + <relationship name="key" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="record" inverseEntity="PersistentKey"/> + <relationship name="persistentMails" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PersistentMail" inverseName="record" inverseEntity="PersistentMail"/> + </entity> + <entity name="Mail_Address" representedClassName="Mail_Address" syncable="YES"> + <attribute name="address" attributeType="String" defaultValueString=""""/> + <attribute name="invitations" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="primaryKeyID" optional="YES" attributeType="String"/> + <attribute name="pseudonym" attributeType="String"/> + <relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="aliase" inverseEntity="Account"/> + <relationship name="bcc" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="bcc" inverseEntity="PersistentMail"/> + <relationship name="cc" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="cc" inverseEntity="PersistentMail"/> + <relationship name="contact" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="EnzevalosContact" inverseName="addresses" inverseEntity="EnzevalosContact"/> + <relationship name="from" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="from" inverseEntity="PersistentMail"/> + <relationship name="keys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="mailaddress" inverseEntity="PersistentKey"/> + <relationship name="to" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="to" inverseEntity="PersistentMail"/> + </entity> + <entity name="PersistentKey" representedClassName="PersistentKey" syncable="YES"> + <attribute name="autocryptGossip" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="discoveryDate" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="encryptionType" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="gossip_timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="gotFailedCallForUse" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="isMisstrusted" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="isRepealed" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="keyID" attributeType="String"/> + <attribute name="lastSeen" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="lastSeenAutocrypt" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="origin" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="preferEncryption" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="pseudonym" attributeType="String"/> + <attribute name="sentOwnPublicKey" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="verifiedDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <relationship name="activeKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentKey"/> + <relationship name="activeRepeal" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail"/> + <relationship name="childKeys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="parentKey" inverseEntity="PersistentKey"/> + <relationship name="firstMail" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="attachedKeys" inverseEntity="PersistentMail"/> + <relationship name="mailaddress" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="keys" inverseEntity="Mail_Address"/> + <relationship name="parentKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="childKeys" inverseEntity="PersistentKey"/> + <relationship name="record" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="KeyRecord" inverseName="key" inverseEntity="KeyRecord"/> + <relationship name="repealedByMails" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="repealsKey" inverseEntity="PersistentMail"/> + <relationship name="signedMails" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="signedKey" inverseEntity="PersistentMail"/> + </entity> + <entity name="PersistentMail" representedClassName="PersistentMail" syncable="YES"> + <attribute name="attachedSignature" optional="YES" attributeType="Binary"/> + <attribute name="body" optional="YES" attributeType="String"/> + <attribute name="date" attributeType="Date" defaultDateTimeInterval="-31582140" usesScalarValueType="NO"/> + <attribute name="decryptedBody" optional="YES" attributeType="String"/> + <attribute name="decrytionCode" optional="YES" attributeType="String"/> + <attribute name="deleteWhileTravel" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="encryptedBody" optional="YES" attributeType="String"/> + <attribute name="flag" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="gmailMessageID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="gmailThreadID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="isCorrectlySigned" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="isEncrypted" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="isKeyImported" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="isSigned" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="keyID" optional="YES" attributeType="String"/> + <attribute name="messageID" optional="YES" attributeType="String"/> + <attribute name="modSeqValue" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="notLoadedMessageIDs" optional="YES" attributeType="String"/> + <attribute name="received" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/> + <attribute name="secretKey" optional="YES" attributeType="String"/> + <attribute name="storeEncrypted" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="subject" attributeType="String"/> + <attribute name="trouble" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="uid" attributeType="Decimal" defaultValueString="0"/> + <attribute name="uidvalidity" optional="YES" attributeType="Decimal" defaultValueString="0.0"/> + <attribute name="unableToDecrypt" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="visibleBody" optional="YES" attributeType="String"/> + <attribute name="xMailer" optional="YES" attributeType="String"/> + <relationship name="attachedKeys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="firstMail" inverseEntity="PersistentKey"/> + <relationship name="attachments" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Attachment" inverseName="mail" inverseEntity="Attachment"/> + <relationship name="bcc" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="bcc" inverseEntity="Mail_Address"/> + <relationship name="cc" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="cc" inverseEntity="Mail_Address"/> + <relationship name="decryptedKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SecretKey" inverseName="decryptedMails" inverseEntity="SecretKey"/> + <relationship name="folder" maxCount="1" deletionRule="Nullify" destinationEntity="Folder" inverseName="mails" inverseEntity="Folder"/> + <relationship name="from" maxCount="1" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="from" inverseEntity="Mail_Address"/> + <relationship name="record" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="KeyRecord" inverseName="persistentMails" inverseEntity="KeyRecord"/> + <relationship name="referenceMails" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="referenceMails" inverseEntity="PersistentMail"/> + <relationship name="relatedSecretKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SecretKey" inverseName="relatedDecryptedMails" inverseEntity="SecretKey"/> + <relationship name="repealsKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="repealedByMails" inverseEntity="PersistentKey"/> + <relationship name="signedKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="signedMails" inverseEntity="PersistentKey"/> + <relationship name="to" toMany="YES" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="to" inverseEntity="Mail_Address"/> + <fetchIndex name="byDateIndex"> + <fetchIndexElement property="date" type="Binary" order="ascending"/> + </fetchIndex> + </entity> + <entity name="SecretKey" representedClassName="SecretKey" syncable="YES"> + <attribute name="exported" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="importedDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="keyID" attributeType="String" defaultValueString="no"/> + <attribute name="obsolete" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="origin" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="keys" inverseEntity="Account"/> + <relationship name="decryptedMails" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="decryptedKey" inverseEntity="PersistentMail"/> + <relationship name="relatedDecryptedMails" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="relatedSecretKey" inverseEntity="PersistentMail"/> + </entity> + <entity name="Server" representedClassName="Server" syncable="YES" codeGenerationType="class"> + <attribute name="authType" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="connectionType" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="hostname" attributeType="String"/> + <attribute name="port" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <relationship name="imap" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="imap" inverseEntity="Account"/> + <relationship name="smtp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="smtp" inverseEntity="Account"/> + </entity> + <fetchRequest name="allKeyRecords" entity="KeyRecord" predicateString="NOT (FALSEPREDICATE)" returnDistinctResults="YES"/> + <fetchRequest name="getFolder" entity="Folder" predicateString="name == "$folder""/> + <fetchRequest name="getMailAddress" entity="Mail_Address" predicateString="address == "$adr""/> + <elements> + <element name="Account" positionX="-315" positionY="-36" width="128" height="255"/> + <element name="Attachment" positionX="-315" positionY="-36" width="128" height="210"/> + <element name="EnzevalosContact" positionX="-209" positionY="198" width="128" height="120"/> + <element name="Folder" positionX="-297" positionY="-18" width="128" height="240"/> + <element name="KeyRecord" positionX="-315" positionY="-36" width="128" height="135"/> + <element name="Mail_Address" positionX="-297" positionY="-18" width="128" height="210"/> + <element name="PersistentKey" positionX="-315" positionY="-36" width="128" height="405"/> + <element name="PersistentMail" positionX="-416" positionY="-189" width="128" height="658"/> + <element name="SecretKey" positionX="-306" positionY="-27" width="128" height="163"/> + <element name="Server" positionX="-306" positionY="-27" width="128" height="135"/> + </elements> +</model> \ No newline at end of file diff --git a/enzevalos_iphone/mail/IncomingMail.swift b/enzevalos_iphone/mail/IncomingMail.swift index 1b63dd842c058138db22097eb961c71231b7e283..77eb67af441dd2e22bafd0652ff5a7e4bdf45c1d 100644 --- a/enzevalos_iphone/mail/IncomingMail.swift +++ b/enzevalos_iphone/mail/IncomingMail.swift @@ -155,6 +155,7 @@ class IncomingMail { return keyIds } } + private var attachedSignature: Data? = nil private var subject: String = "" private var date: Date = Date() private var autocrypt: Autocrypt? = nil @@ -181,6 +182,7 @@ class IncomingMail { private var body: String = "" private var encryptedBody: String? private var secretKeys: [String] = [] + private var relaedSecrectKey: SecretKey? = nil private var newPublicKeys: [String] = [] private var newAutocrypPublicKeys: [String] = [] private var newSecretKeyIDs: [String]? = [] @@ -264,7 +266,7 @@ class IncomingMail { func store(keyRecord: KeyRecord?) -> PersistentMail? { let sk = secretKeys.first //TODO FIX: may import more secret keys? - let mail = DataHandler.handler.createMail(uID, sender: from, receivers: rec, cc: cc, time: date, received: true, subject: subject, body: body, readableAttachments: readableAttachments, flags: flags, record: keyRecord, autocrypt: autocrypt, decryptedData: cryptoObj, folderPath: folderPath, secretKey: sk, references: references, mailagent: userAgent, messageID: msgID, encryptedBody: encryptedBody, storeEncrypted: storeEncrypted) + let mail = DataHandler.handler.createMail(uID, sender: from, receivers: rec, cc: cc, time: date, received: true, subject: subject, body: body, readableAttachments: readableAttachments, flags: flags, record: keyRecord, autocrypt: autocrypt, decryptedData: cryptoObj, folderPath: folderPath, secretKey: sk, references: references, mailagent: userAgent, messageID: msgID, encryptedBody: encryptedBody, storeEncrypted: storeEncrypted, attachedSignature: attachedSignature) if let m = mail { let pgp = SwiftPGP() if let autoc = autocrypt, let adr = from?.mailbox { @@ -375,6 +377,7 @@ class IncomingMail { signedStrings = IncomingMail.extractSignedMessage(text: body) inlineSigned = true } + var text: String? var signedObject: CryptoObject? if signedStrings.count == 1 { @@ -383,7 +386,11 @@ class IncomingMail { else if signedStrings.count > 1 { text = signedStrings.joined(separator: "\r\n") } - if let signedData = text?.data(using: .utf8){ // No Signed Data! + if let signedData = text?.data(using: .utf8) { // No Signed Data! + // First Signature as main attachedSignature for mail -- not optimal + if let sig = signaturesRaw.first { + self.attachedSignature = try? Armor.readArmored(sig) + } for sig in signaturesRaw { if let signature = try? Armor.readArmored(sig), let adr = from?.mailbox { for id in fromKeyIds { diff --git a/enzevalos_iphoneTests/CryptoTests.swift b/enzevalos_iphoneTests/CryptoTests.swift index a3695a8eeb2efb5542ee570b0384ff8854959d89..06cbf1c028d152034c6a43823869733a5ea7082e 100644 --- a/enzevalos_iphoneTests/CryptoTests.swift +++ b/enzevalos_iphoneTests/CryptoTests.swift @@ -591,6 +591,76 @@ class CryptoTests: XCTestCase { return "" } + 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)) -> PersistentMail? { + + let subject = String.random(length: 20) + let id = UInt64(arc4random()) + var body = body + + if let decryptedBody = cryptoObject?.decryptedText { + body = decryptedBody + } + var mail: PersistentMail? + 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) + XCTAssertNotNil(mail) + XCTAssertEqual(mail?.body, body) + XCTAssertEqual(mail?.subject, subject) + XCTAssertEqual(mail?.folder.name.lowercased(), folder.lowercased()) + + return mail + } + + // Not finished yet + func testfindNotSignedMailForPublicKey() { + // E-Mail generieren + guard let from = MCOAddress(mailbox: "alice@example.com") else { + return + } + // Create test Mail + guard let mail = testMail(from: from, to: [user], cc: [], bcc: []) else { + XCTFail("No test mail") + return + } + // import keys and data for signature test + guard let keys = try? pgp.importKeys(key: keyForSignedMessage, pw: nil, isSecretKey: true, autocrypt: false), keys.count > 0 else { + XCTFail("Can not import key") + return + } + guard let signedData = try? Armor.readArmored(signedMessage) else { + XCTFail("No signed data") + return + } + guard let publickey = pgp.loadKey(id: mail.firstKey!.keyID)?.publicKey, keys.count > 0 else { + XCTFail("Can not load key") + return + } + + let text = "only a signed mail!" + + // Change Mail + mail.secretKey = keys.first + mail.isSigned = true + mail.isCorrectlySigned = false + datahandler.save(during: "") + XCTAssertTrue(mail.isSigned) + XCTAssertFalse(mail.isCorrectlySigned) + + // "un-comment" when signature is generated correctly for test email. + // pgp.findNotSignedMailForPublicKey(keyID: publickey.keyID.longIdentifier) + XCTAssertTrue(mail.isSigned) + XCTAssertTrue(mail.isCorrectlySigned) + + // try to decrypt data + if let data = text.data(using: .utf8) { + let cryptoObject = pgp.decrypt(data: data, attachedSignature: signedData, decKeyIDs: [userKeyID], signatureIDs: keys, fromAddr: "alice@enzevalos.de") + XCTAssert(cryptoObject.encryptionState == .NoEncryption && cryptoObject.signatureState == .ValidSignature, "EncState: \(cryptoObject.encryptionState) SigState: \(cryptoObject.signatureState)") + XCTAssert(cryptoObject.chiperString == text) + } + else { + XCTFail("Can not make String to data.") + } + } + func testImportMultiIDs(){ let key = importKey(file: "AliceMultiIDs (439EE43C) – Public") do { diff --git a/enzevalos_iphoneTests/GeneratedMocks.swift b/enzevalos_iphoneTests/GeneratedMocks.swift index e1c29d05ac5c585aa6fc7be9d218a635762c565c..9aad2c9cde713fb651b74a3a03b2ded3943c8b53 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 2020-03-31 15:54:34 +0000 +// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationModel.swift at 2020-03-18 15:11:52 +0000 // // AuthenticationModel.swift @@ -653,7 +653,7 @@ import Foundation } -// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationViewModel.swift at 2020-03-31 15:54:34 +0000 +// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationViewModel.swift at 2020-03-18 15:11:52 +0000 // // AuthenticationViewModel.swift diff --git a/enzevalos_iphoneTests/phishing/EmailStringExtensionTests.swift b/enzevalos_iphoneTests/phishing/EmailStringExtensionTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..bfe4779c689e28e83678bc8d4198f6ce9cc62158 --- /dev/null +++ b/enzevalos_iphoneTests/phishing/EmailStringExtensionTests.swift @@ -0,0 +1,141 @@ + // + // EmailAdressTests.swift + // enzevalos_iphoneTests + // + // Created by Katharina Müller on 17.03.20. + // Copyright © 2020 fu-berlin. All rights reserved. + // + import XCTest + @testable import enzevalos_iphone + + class EmailStringExtensionTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + // General TestText for all (needs to simulate a body of text from which to extract specific Strings + var TestText = "blabla moep jkjlkj lkjkljknmbjks llhil k. jhkhkih. huhuhj! fsdf bob.alice@zedat.fu-berlin.de dfsf. jhjknjknjkh https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240 hjkhjkhkhn www.google.de kljhl@hjkghgkhj.com nljbjkjk.de url tag html mail <a href='https://www.w3schools.com'>Visit W3Schools</a> hjhkhiuhziu kjhkl <a href=\"https://www.w2schools.com\">Visit W2Schools</a>. lknljnlk. kmm /n lmölmpöl < a href=\"https://www.w8schools.com\">Visit W8Schools</a> gfg fghfghnhg <a href=\"https://www.w33schools.com\">Visit W33Schools</a> nkjhjkhkjn,mn jnmnklmj j <a href=\"https://www.w22schools.com\">Visit W22Schools</ a> hghjcfgh hfgchnvhj vgjcgj cjghcj <a href=\"https://www.w99schools.com\">Visit W99Schools</a > 你好@yahoo.com eewfve test@莎士比亚.org 你好@莎士比亚.org Rδοκιμή@παράδειγμα.δοκιμή or 管理员@中国互联网络信息中心.中国 你好45@yahoo.com" + + // setup for find mail address in text + var finalMailAddresses = ["bob.alice@zedat.fu-berlin.de", + "kljhl@hjkghgkhj.com", + "你好@yahoo.com", + "test@莎士比亚.org", + "你好@莎士比亚.org", + "Rδοκιμή@παράδειγμα.δοκιμή", + "管理员@中国互联网络信息中心.中国", + "你好45@yahoo.com"] + + func testFindEmails(){ + let arr = TestText.findEmails() + XCTAssertNotNil(arr) + XCTAssertEqual(arr[0], finalMailAddresses[0]) + XCTAssertEqual(arr[1], finalMailAddresses[1]) + XCTAssertEqual(arr[2], finalMailAddresses[2]) + XCTAssertEqual(arr[3], finalMailAddresses[3]) + XCTAssertEqual(arr[4], finalMailAddresses[4]) + XCTAssertEqual(arr[5], finalMailAddresses[5]) + XCTAssertEqual(arr[6], finalMailAddresses[6]) + XCTAssertEqual(arr[7], finalMailAddresses[7]) + } + + // setup email validation + var validEmails = ["alice.bob@moep.blubb.de", + "test@google.com", + "test@google.co.uk", + "你好@yahoo.com", + "test@莎士比亚.org", + "你好@莎士比亚.org", + "Rδοκιμή@παράδειγμα.δοκιμή", + "管理员@中国互联网络信息中心.中国", + "你好45@yahoo.com"] + + var notValidEmails = ["test@@google.com", + "test@google", + "Abc.example.com", + "A@b@c@example.com", + "this is\"not\\allowed@example.com", + "this\\ still\\\"not\\\\allowed@example.com", + "1234567890123456789012345678901234567890123456789012345678901234+x@example.com"] + ///Finding Email adresses etc. + func testIsValidEmail(){ + for v in validEmails { + XCTAssertTrue(v.isValidEmail()) + } + for n in notValidEmails{ + XCTAssertFalse(n.isValidEmail()) + } + } + + // setup for mail address splitting + var correctSplit_0 = ["alice.bob", "moep", "blubb", "de"] + var correctSplit_1 = ["test", "google", "com"] + var correctSplit_2 = ["test", "google", "co", "uk"] + var correctSplit_3 = ["你好", "yahoo", "com"] + var correctSplit_4 = ["test", "莎士比亚", "org"] + var correctSplit_5 = ["你好", "莎士比亚", "org"] + var correctSplit_6 = ["Rδοκιμή", "παράδειγμα", "δοκιμή"] + var correctSplit_7 = ["管理员", "中国互联网络信息中心", "中国"] + var correctSplit_8 = ["你好45", "yahoo", "com"] + var spliEmails:[Any] { + return [correctSplit_0, correctSplit_1, correctSplit_2, correctSplit_3, correctSplit_4, correctSplit_5, correctSplit_6, correctSplit_7, correctSplit_8] + } + + func testSplitMailAddress(){ + for (i, email) in validEmails.enumerated(){ + let arr = (email.splitAddress()) + XCTAssertNotNil(arr) + for (j, _) in arr.enumerated() + { + let array = spliEmails[i] as? [String] + XCTAssertEqual(arr[j], array?[j]) + } + } + } + + var finalTestEMailIdentity = ["alice.bob", "test", "test", "你好", "test", "你好", "Rδοκιμή", "管理员", "你好45"] + func testGetMailIdentity(){ + for (i, email) in validEmails.enumerated(){ + let arr = (email.getLocalMailIdentity()) + XCTAssertNotNil(arr) + XCTAssertEqual(arr, finalTestEMailIdentity[i]) + } + } + + var eMailSubDomain_0 = ["moep", "blubb"] + var eMailSubDomain_1 = ["παράδειγμα"] + var eMailSubDomain_2 = ["中国互联网络信息中心"] + var eMailSubDomain_3 = ["google", "co"] + func testGetSubdomains(){ + let arr_0 = validEmails[0].getSubdomains() + XCTAssertNotNil(arr_0) + XCTAssertEqual(arr_0[0], eMailSubDomain_0[0]) + XCTAssertEqual(arr_0[1], eMailSubDomain_0[1]) + + let arr_1 = validEmails[6].getSubdomains() + XCTAssertNotNil(arr_1) + XCTAssertEqual(arr_1[0], eMailSubDomain_1[0]) + + let arr_2 = validEmails[7].getSubdomains() + XCTAssertNotNil(arr_2) + XCTAssertEqual(arr_2[0], eMailSubDomain_2[0]) + + let arr_3 = validEmails[2].getSubdomains() + XCTAssertNotNil(arr_3) + XCTAssertEqual(arr_3[0], eMailSubDomain_3[0]) + XCTAssertEqual(arr_3[1], eMailSubDomain_3[1]) + } + + var finalTestEMailDomain = ["de", "com", "uk", "com", "org", "org", "δοκιμή", "中国", "com"] + func testGetDomain(){ + for (i, email) in validEmails.enumerated(){ + let arr = (email.getDomain()) + XCTAssertNotNil(arr) + XCTAssertEqual(arr, finalTestEMailDomain[i]) + } + } + } diff --git a/enzevalos_iphoneTests/PhishingTests.swift b/enzevalos_iphoneTests/phishing/UrlStringExtensionTests.swift similarity index 67% rename from enzevalos_iphoneTests/PhishingTests.swift rename to enzevalos_iphoneTests/phishing/UrlStringExtensionTests.swift index 1927629ab39f5730855c63ec88dd606ea83e3870..8bcabf7dc73b30376b1c1a47ae5734baebccb684 100644 --- a/enzevalos_iphoneTests/PhishingTests.swift +++ b/enzevalos_iphoneTests/phishing/UrlStringExtensionTests.swift @@ -5,77 +5,32 @@ // Created by Viktoria Sorgalla on 02.03.20. // Copyright © 2020 fu-berlin. All rights reserved. // - import XCTest @testable import enzevalos_iphone -class PhishingTests: XCTestCase { - -// setup for mail address splitting - var testEMail = "alice.bob@moep.blubb.de" - - var finalTestEMailSplitAddress = ["alice.bob", "moep", "blubb", "de"] - - var finalTestEMailIdentity = "alice.bob" - - var finalTestEMailSubDomains = ["moep", "blubb"] - - var finalTestEMailDomain = "de" - - var TestText = "blabla moep jkjlkj lkjkljknmbjks llhil k. jhkhkih. huhuhj! fsdf bob.alice@zedat.fu-berlin.de dfsf. jhjknjknjkh https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240 hjkhjkhkhn www.google.de kljhl@hjkghgkhj.com nljbjkjk.de url tag html mail <a href='https://www.w3schools.com'>Visit W3Schools</a> hjhkhiuhziu kjhkl <a href=\"https://www.w2schools.com\">Visit W2Schools</a>. lknljnlk. kmm /n lmölmpöl < a href=\"https://www.w8schools.com\">Visit W8Schools</a> gfg fghfghnhg <a href=\"https://www.w33schools.com\">Visit W33Schools</a> nkjhjkhkjn,mn jnmnklmj j <a href=\"https://www.w22schools.com\">Visit W22Schools</ a> hghjcfgh hfgchnvhj vgjcgj cjghcj <a href=\"https://www.w99schools.com\">Visit W99Schools</a >" +class UrlStringExtensionTests: XCTestCase { -// setup for find mail address in text - var finalMailAddresses = ["bob.alice@zedat.fu-berlin.de", "kljhl@hjkghgkhj.com"] + // General TestText for all (needs to simulate a body of text from which to extract specific Strings + var TestText = "blabla moep jkjlkj lkjkljknmbjks llhil k. jhkhkih. huhuhj! fsdf bob.alice@zedat.fu-berlin.de dfsf. jhjknjknjkh https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240 hjkhjkhkhn www.google.de kljhl@hjkghgkhj.com nljbjkjk.de url tag html mail <a href='https://www.w3schools.com'>Visit W3Schools</a> hjhkhiuhziu kjhkl <a href=\"https://www.w2schools.com\">Visit W2Schools</a>. lknljnlk. kmm /n lmölmpöl < a href=\"https://www.w8schools.com\">Visit W8Schools</a> gfg fghfghnhg <a href=\"https://www.w33schools.com\">Visit W33Schools</a> nkjhjkhkjn,mn jnmnklmj j <a href=\"https://www.w22schools.com\">Visit W22Schools</ a> hghjcfgh hfgchnvhj vgjcgj cjghcj <a href=\"https://www.w99schools.com\">Visit W99Schools</a > 你好@yahoo.com eewfve test@莎士比亚.org 你好@莎士比亚.org Rδοκιμή@παράδειγμα.δοκιμή or 管理员@中国互联网络信息中心.中国 你好45@yahoo.com" -// setup for find URLs in text + // setup for find URLs in text var finalURLs = ["https://git.imp.fu-berlin.de/enzevalos/enzevalos_iphone/issues/240", "http://www.google.de", "http://nljbjkjk.de", "https://www.w3schools.com", "https://www.w2schools.com", "https://www.w8schools.com", "https://www.w33schools.com", "https://www.w22schools.com", "https://www.w99schools.com"] -// setup for tag tests - var finalTestTextFindHtmlTags = ["<a href=\'https://www.w3schools.com\'>Visit W3Schools</a>", "<a href=\"https://www.w2schools.com\">Visit W2Schools</a>", "< a href=\"https://www.w8schools.com\">Visit W8Schools</a>", "<a href=\"https://www.w33schools.com\">Visit W33Schools</a>", "<a href=\"https://www.w22schools.com\">Visit W22Schools</ a>", "<a href=\"https://www.w99schools.com\">Visit W99Schools</a >"] + // setup for tag tests + var finalTestTextFindHtmlTags = ["<a href=\'https://www.w3schools.com\'>Visit W3Schools</a>", "<a href=\"https://www.w2schools.com\">Visit W2Schools</a>", "< a href=\"https://www.w8schools.com\">Visit W8Schools</a>", "<a href=\"https://www.w33schools.com\">Visit W33Schools</a>", "<a href=\"https://www.w22schools.com\">Visit W22Schools</ a>", "<a href=\"https://www.w99schools.com\">Visit W99Schools</a >"] var finalTestTextHtmlTagURL = ["https://www.w3schools.com", "https://www.w2schools.com", "https://www.w8schools.com", "https://www.w33schools.com", "https://www.w22schools.com", "https://www.w99schools.com"] var finalTestTexthttpTagLinkName = ["Visit W3Schools", "Visit W2Schools", "Visit W8Schools", "Visit W33Schools", "Visit W22Schools", "Visit W99Schools"] + var testUrl = "www.google.de" + override func setUp() { super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. } - + override func tearDown() { super.tearDown() - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testSplitMailAddress(){ - let arr = testEMail.splitAddress() - XCTAssertNotNil(arr) - XCTAssertEqual(arr[0], finalTestEMailSplitAddress[0]) - XCTAssertEqual(arr[1], finalTestEMailSplitAddress[1]) - XCTAssertEqual(arr[2], finalTestEMailSplitAddress[2]) - XCTAssertEqual(arr[3], finalTestEMailSplitAddress[3]) - } - - func testGetMailIdentity(){ - XCTAssertEqual(testEMail.getLocalMailIdentity(), finalTestEMailIdentity) - } - - func testGetSubdomains(){ - let arr = testEMail.getSubdomains() - XCTAssertNotNil(arr) - XCTAssertEqual(arr[0], finalTestEMailSubDomains[0]) - XCTAssertEqual(arr[1], finalTestEMailSubDomains[1]) - } - - func testGetDomain(){ - XCTAssertEqual(testEMail.getDomain(), finalTestEMailDomain) - } - - func testTextFindMailAddress(){ - let arr = TestText.findMailAddress() - XCTAssertNotNil(arr) - XCTAssertEqual(arr[0], finalMailAddresses[0]) - XCTAssertEqual(arr[1], finalMailAddresses[1]) } func testTextFindURL(){ @@ -86,7 +41,7 @@ class PhishingTests: XCTestCase { XCTAssertEqual(arr[2], finalURLs[2]) XCTAssertEqual(arr[3], finalURLs[3]) } - + func testTextFindHlmlTags(){ let arr = TestText.findHtmlTags() XCTAssertNotNil(arr) @@ -119,4 +74,9 @@ class PhishingTests: XCTestCase { XCTAssertEqual(arr[4], finalTestTexthttpTagLinkName[4]) XCTAssertEqual(arr[5], finalTestTexthttpTagLinkName[5]) } + + func testUrl2Ip(){ + XCTAssertNotNil(testUrl.url2ip()) + //It changes often therefore a direct comparisson is not useful + } }