diff --git a/enzevalos_iphone.xcodeproj/project.pbxproj b/enzevalos_iphone.xcodeproj/project.pbxproj index a21b790f4da008713d73697e764770fb7020aaf2..e0e7277b6e8a7f580f5de516ffd92345577ee309 100644 --- a/enzevalos_iphone.xcodeproj/project.pbxproj +++ b/enzevalos_iphone.xcodeproj/project.pbxproj @@ -1123,6 +1123,7 @@ 4764068C2416B54D00C7D426 /* SupportingViews */ = { isa = PBXGroup; children = ( + 47C8224924379EAE005BCE73 /* DialogView.swift */, 47C822612438A81C005BCE73 /* MapView.swift */, 47EABF2A2423C20C00774A93 /* LoadingBlocker.swift */, 47EABF282423C1FB00774A93 /* KeyboardChecker.swift */, @@ -1282,7 +1283,6 @@ 47C8224824379EAE005BCE73 /* MessageViewChildren */ = { isa = PBXGroup; children = ( - 47C8224924379EAE005BCE73 /* DialogView.swift */, 47C8224A24379EAE005BCE73 /* FloatingActionButton.swift */, ); path = MessageViewChildren; diff --git a/enzevalos_iphone/CryptoObject.swift b/enzevalos_iphone/CryptoObject.swift index 3deca6a04495ff95f59f66a4850a8b09e6779b24..6a3d2964db2c7c894d67303221b8b4e5f15bbddf 100644 --- a/enzevalos_iphone/CryptoObject.swift +++ b/enzevalos_iphone/CryptoObject.swift @@ -6,6 +6,188 @@ // import Foundation +import SwiftUI + +struct SecurityState <M: DisplayMail> { + enum CryptoState { + case UnableToDecrypt, InvalidSignature, NoCrypto, PlainMissingPublicKeyToVerify, PlainButValidSignature, EncValidSign, EncNoSignature, EncButMissingPublicKeyToVerify + } + + let mail: M + + // TODO: Handling new key attack + + var title: String { + get { + var key = "" + switch evaluateSecurity() { + case (_,.NoCrypto): key = "Security.Dialog.Title.No.Crypto" + case (_, .UnableToDecrypt): key = "Security.Dialog.Title.UnableToDecrypt" + case (_, .InvalidSignature): key = "Security.Dialog.Title.InvalidSignature" + case (_, .PlainMissingPublicKeyToVerify): key = "Security.Dialog.Title.PlainMissingPublicKeyToVerify" + case (_, .PlainButValidSignature): key = "Security.Dialog.Title.Plain+Sig" + case (_, .EncValidSign): key = "Security.Dialog.Title.Enc+Sign" + case (_, .EncNoSignature): key = "Security.Dialog.Title.Enc+NoSign" + case (_, .EncButMissingPublicKeyToVerify): key = "Security.Dialog.Title.EncMissingPublicKeyToVerify" + } + return NSLocalizedString(key, comment: "") + } + } + + var icon: Image { + get { + switch evaluateSecurity() { + case (_, .EncValidSign): + return StudySettings.securityIndicator.imageOfSecureIndicatorSwiftUI() + case (_, .UnableToDecrypt), (_, .InvalidSignature): + return StudySettings.securityIndicator.imageOfCorruptedIndicatorSwiftUI() + case (_, .NoCrypto), (_, .PlainMissingPublicKeyToVerify), (_, .PlainButValidSignature), (_, .EncNoSignature), (_, .EncButMissingPublicKeyToVerify): + return StudySettings.securityIndicator.imageOfInsecureIndicatorSwiftUI() + } + } + } + + var dialog: DialogStruct { + get { + // TODO: Do we add new public key stuff? + var color: UIColor + var bodyKey: String + let infoTitle = NSLocalizedString("Security.Dialog.Button.Title.MoreInfo", comment: "") + + var ctaButtonTitleKey: String = "Security.Dialog.Button.Title.OK" + var ctaButtonAction: ButtonAction = .OK + + var moreButtons: [ButtonStruct] = [] + + var dismissButtonTitleKey: String? + var dismissButtonAction: ButtonAction? + + switch evaluateSecurity() { + case (_, .EncValidSign): + color = ThemeManager.encryptedMessageColor() + bodyKey = "OnboardingIntroSection.Confidential.description" + // ctaButton -> Ok + + case (_, .UnableToDecrypt): + color = ThemeManager.troubleMessageColor() + bodyKey = "ReceiveInsecureInfoDecryptionFailed" + // cta Button -> OK + // add -> Import Key + var btn = ButtonStruct(titleKey: "Security.Dialog.Button.Title.Import.SK", action: .AskUserToImportSK) + moreButtons.append(btn) + // add -> Ask to resend for differnet PK + btn = ButtonStruct(titleKey: "Security.Dialog.Button.Title.Ask.Resend.ForPK", action: .AskSenderToResendForPK) + moreButtons.append(btn) + + case (_, .InvalidSignature): + color = ThemeManager.troubleMessageColor() + bodyKey = "ReceiveDamagedInfo" + // cta Button -> Ignore Mail + ctaButtonTitleKey = "Security.Dialog.Button.Title.Ignore.Mail" + ctaButtonAction = .IgnoreMail + // add -> ask sender + let btn = ButtonStruct(titleKey: "Security.Dialog.Button.Title.Confirmation", action: .AskSenderToConfirm) + moreButtons.append(btn) + // dismiss -> Ignore Warning + dismissButtonTitleKey = "Security.Dialog.Button.Title.OK" + dismissButtonAction = .IgnoreWarning + + case (_, .NoCrypto): + color = ThemeManager.unencryptedMessageColor() + bodyKey = "ReceiveInsecureInfo" + // cta Button -> Invite user + ctaButtonTitleKey = "Security.Dialog.Button.Title.Invite" + ctaButtonAction = .InvitePerson + // add -> OK + let btn = ButtonStruct(titleKey: "Security.Dialog.Button.Title.OK", action: .OK) + moreButtons.append(btn) + + case (_, .PlainMissingPublicKeyToVerify): + color = ThemeManager.unencryptedMessageColor() + bodyKey = "Security.Dialog.Body.PlainMissingPublicKeyToVerify" + // cta Button -> Ask to send PK + ctaButtonTitleKey = "Security.Dialog.Button.Title.Ask.ForPK" + ctaButtonAction = .AskSenderToSendPK + // add -> OK + let btn = ButtonStruct(titleKey: "Security.Dialog.Button.Title.OK", action: .OK) + moreButtons.append(btn) + + case (_, .PlainButValidSignature): + color = ThemeManager.unencryptedMessageColor() + bodyKey = "ReceiveInsecureInfoVerified" + // cta Button -> send PK + ctaButtonTitleKey = "Security.Dialog.Button.Title.Send.PK" + ctaButtonAction = .SendPK + // add -> OK + let btn = ButtonStruct(titleKey: "Security.Dialog.Button.Title.OK", action: .OK) + moreButtons.append(btn) + + case (_, .EncNoSignature): + color = ThemeManager.unencryptedMessageColor() + bodyKey = "Information.General.OnlyEncryted" + // cta button -> Ask to confirm + // add -> OK + let btn = ButtonStruct(titleKey: "Security.Dialog.Button.Title.OK", action: .OK) + moreButtons.append(btn) + + case (_, .EncButMissingPublicKeyToVerify): + color = ThemeManager.unencryptedMessageColor() + bodyKey = "Security.Dialog.Body.EncButMissingPublicKeyToVerify" + // cta Button -> Ask to send PK + ctaButtonTitleKey = "Security.Dialog.Button.Title.Ask.ForPK" + ctaButtonAction = .AskSenderToSendPK + // add -> OK + let btn = ButtonStruct(titleKey: "Security.Dialog.Button.Title.OK", action: .OK) + moreButtons.append(btn) + } + + let body = NSLocalizedString(bodyKey, comment: "") + let ctaButtonTitle = NSLocalizedString(ctaButtonTitleKey, comment: "") + var dismissButtonTitle: String? + if let key = dismissButtonTitleKey { + dismissButtonTitle = NSLocalizedString(key, comment: "") + } + + return DialogStruct(dialogColor: Color(color), title: title, body: body, img: icon, messageImage: nil, ctaButtonTitle: ctaButtonTitle, ctaButtonAction: ctaButtonAction, infoButtonTitle: infoTitle, moreButtons: moreButtons, dismissButtonTitle: dismissButtonTitle, dismissButtonAction: dismissButtonAction) + } + } + + var warnings: [DialogStruct] { + get { + return [] + } + } + + private func evaluateSecurity() -> (isPhish: Bool, cryptoState: CryptoState) { + // TODO Phishing handling + let isPhish = false + let cryptoState = evaluateCryptoState() + + return (isPhish, cryptoState) + } + + private func evaluateCryptoState() -> CryptoState { + switch (mail.encState, mail.signedState) { + // General error cases -> cast other states + case (.UnableToDecrypt, _): return .UnableToDecrypt + case (_, .InvalidSignature): return .InvalidSignature + case (.NoEncryption, .NoSignature): return .NoCrypto + case (.NoEncryption, .NoPublicKey): return .PlainMissingPublicKeyToVerify + case (.NoEncryption, .ValidSignature): return .PlainButValidSignature + case (.ValidEncryptedWithOldKey, .ValidSignature): return .EncValidSign + case (.ValidedEncryptedWithCurrentKey, .ValidSignature): return .EncValidSign + case (.ValidEncryptedWithOldKey, .NoSignature): return .EncNoSignature + case (.ValidEncryptedWithOldKey, .NoPublicKey): return .EncButMissingPublicKeyToVerify + case (.ValidedEncryptedWithCurrentKey, .NoSignature): return .EncNoSignature + case (.ValidedEncryptedWithCurrentKey, .NoPublicKey): return .EncButMissingPublicKeyToVerify + } + + } + + +} + + enum SignatureState: Int16 { case NoSignature = 0 // -> no authenticity -> no actions possible @@ -13,7 +195,7 @@ enum SignatureState: Int16 { case InvalidSignature = -1 // -> ERROR -> context is manipulated (either attack or MTA...) case ValidSignature = 2 - var name: String { + var loggingTag: String { get { switch self { case .NoSignature: @@ -36,7 +218,7 @@ enum EncryptionState: Int16 { case ValidEncryptedWithOldKey = 2 case ValidedEncryptedWithCurrentKey = 3 - var name: String { + var loggingTag: String { get { switch self { case .NoEncryption: @@ -73,6 +255,8 @@ public enum CryptoScheme { switch i { case 0: return CryptoScheme.PGP + case 1: + return CryptoScheme.SMIME default: return CryptoScheme.UNKNOWN } diff --git a/enzevalos_iphone/Dialog/DialogOption.swift b/enzevalos_iphone/Dialog/DialogOption.swift index 9adff83503d2b5db084c1168a4783ea5d1dbe983..0ff9b70da07ec4504019c505e22dad7b90e4224a 100644 --- a/enzevalos_iphone/Dialog/DialogOption.swift +++ b/enzevalos_iphone/Dialog/DialogOption.swift @@ -22,50 +22,7 @@ import UIKit import SwiftUI /* TODO: MORE DIALOGS - if mail.trouble{ - Text(NSLocalizedString("corruptedHeadline", comment: "corrupted mail")) - .font(.system(size: 25)) - .frame(maxWidth: .infinity) - .background(Color.yellow) - HStack{ - Text("!") - .foregroundColor(Color.red) - .font(.system(size: 60)) - .padding([.leading, .top, .bottom]) - Text(NSLocalizedString("corruptedText", comment: "corrupted mail")) - .padding() - } - } - else if mail.isEncrypted && mail.unableToDecrypt{ - if TravelHandler.instance().mode != .atHome{ - Text(NSLocalizedString("couldNotDecryptTravelHeadline", comment: "couldn't decrypt message")) - .font(.system(size: 25)) - .frame(maxWidth: .infinity) - .background(Color.yellow) - HStack{ - Text("?") - .foregroundColor(Color.orange) - .font(.system(size: 60)) - .padding([.leading, .top, .bottom]) - Text(NSLocalizedString("couldNotDecryptTravelText", comment: "couldn't decrypt message")) - .padding() - } - } - else { - Text(NSLocalizedString("couldNotDecryptHeadline", comment: "couldn't decrypt message")) - .font(.system(size: 25)) - .frame(maxWidth: .infinity) - .background(Color.yellow) - HStack{ - Text("?") - .foregroundColor(Color.orange) - .font(.system(size: 60)) - .padding([.leading, .top, .bottom]) - Text(NSLocalizedString("couldNotDecryptText", comment: "couldn't decrypt message")) - .padding() - } - } - } + //else if (mail.isNewPubKey) && !(mail.deleteWhileTravel){} // message contained new public key else if mail.from.hasKey && !mail.isSecure{ @@ -82,14 +39,107 @@ import SwiftUI .padding() } } - //else if mail.deleteWhileTravel{} - // message not readable while traveling -}.background(Color(UIColor.systemGray5)).padding() -} */ -enum DialogOption { +protocol Dialog { + var dialogColor: Color { get } + var title: String { get } + var body: String { get } + var img: Image { get } + var ctaButtonTitle: String? { get } + var ctaButtonAction: ButtonAction? { get } + var infoButtonTitle: String? { get } + + var moreButtons: [ButtonStruct] { get } + + var dismissButtonTitle: String? { get } + var dismissButtonAction: ButtonAction? { get } + +} + +struct ButtonStruct { + let titleKey: String + let action: ButtonAction + + var title: String { + get { + return NSLocalizedString(titleKey, comment: "") + } + } +} + +struct DialogStruct: Dialog { + + let dialogColor: Color + + let title: String + let body: String + + let img: Image + let messageImage: Image? + + let ctaButtonTitle: String? + let ctaButtonAction: ButtonAction? + + let infoButtonTitle: String? + + let moreButtons: [ButtonStruct] + + let dismissButtonTitle: String? + let dismissButtonAction: ButtonAction? +} + +enum ButtonAction { + case AskSenderToConfirm, AskSenderToSendPK, AskSenderToResendForPK, InvitePerson, IgnoreMail, AskUserToImportSK, ImportSK, ImportPK, MoreInformation, ExportSK, OK, IgnoreWarning, SendPK +} + +enum DialogOption: Dialog { + var dialogColor: Color { + get { + return Color(color) + } + } + + var body: String { + get { + return message + } + } + + var img: Image { + get{ + if let image = self.messageImage { + return image + } + return Image(systemName: "exclamationmark.triangle.fill") + } + } + + var ctaButtonAction: ButtonAction? { + get { + return nil + } + } + + var infoButtonTitle: String? { + get { + return nil + } + } + + var moreButtons: [ButtonStruct] { + get { + return [] + } + } + + var dismissButtonAction: ButtonAction? { + get { + return nil + } + } + case postcard case invitationCode(code: String) diff --git a/enzevalos_iphone/Logger.swift b/enzevalos_iphone/Logger.swift index 03682a6dc6ce05c3b3394c0cadf80390c4f549e4..23a4c12093d7dd98a3462660672cb4e8127c02b5 100644 --- a/enzevalos_iphone/Logger.swift +++ b/enzevalos_iphone/Logger.swift @@ -442,7 +442,7 @@ public class LogData { } private static func makeMailKey(enc: EncryptionState, sig: SignatureState, received: Bool) -> String { - var key = "\(enc.name)&\(sig.name)" + var key = "\(enc.loggingTag)&\(sig.loggingTag)" if received { key = "in|" + key } diff --git a/enzevalos_iphone/SwiftUI/Read/ReadMainView.swift b/enzevalos_iphone/SwiftUI/Read/ReadMainView.swift index 545d8edf43b8ee0c1198b1c1254e07a1332a153b..b8be6ac846a767e394058515da5808caa21dd1c5 100644 --- a/enzevalos_iphone/SwiftUI/Read/ReadMainView.swift +++ b/enzevalos_iphone/SwiftUI/Read/ReadMainView.swift @@ -20,12 +20,10 @@ import SwiftUI struct ReadMainView <M: DisplayMail>: View { public var mail: M + + @State private var currentScreen: Int = 1 //doesnt work - public var coord: ReadViewCoordinator - - @State var currentScreen: Int = 1 //doesnt work - - @State var isSecIndExpanded:Bool = false + @State private var isSecIndExpanded:Bool = false var body: some View { //TODO: put into zstack instead @@ -46,7 +44,7 @@ struct ReadMainView <M: DisplayMail>: View { // , displayMode: .inline) //TODO: make smooth or find better solution .navigationBarItems(trailing: moreInfoButton) //.onAppear(perform: self.coord.setup) - .onDisappear(perform: self.coord.reset) + //.onDisappear(perform: self.coord.reset) } //TODO: not use AnyView-workaround @@ -237,9 +235,7 @@ struct expandedSecInd: View { #if DEBUG struct Layout_Previews: PreviewProvider { static var previews: some View { - Group { - expandedSecInd(secNum: 2) - } + ReadMainView(mail: mail) } } #endif diff --git a/enzevalos_iphone/SwiftUI/Read/ReadViewCoordinator.swift b/enzevalos_iphone/SwiftUI/Read/ReadViewCoordinator.swift index d7518caaa5db0daaeb834e30098d2ae140c1ec41..487bb4416652b528c1ab5d5613c44ce27b4020a3 100644 --- a/enzevalos_iphone/SwiftUI/Read/ReadViewCoordinator.swift +++ b/enzevalos_iphone/SwiftUI/Read/ReadViewCoordinator.swift @@ -20,7 +20,6 @@ import SwiftUI /* TODOS: - * Remove download -> All files are allready downloaded * Does Views disappear? * Add Security indicator */ @@ -45,7 +44,7 @@ class ReadViewCoordinator { AppDelegate.getAppDelegate().mailHandler.updateFolder(folder: Folder.inbox, completionCallback: {_ in }) - let vc = UIHostingController(rootView: ReadMainView(mail: mail, coord: self)) + let vc = UIHostingController(rootView: ReadMainView(mail: mail)) readView = vc root.pushViewController(vc, animated: true) } @@ -79,9 +78,6 @@ class ReadViewCoordinator { func pushExportKeyView() { //TODO: make this work let vc = mainStoryboard.instantiateViewController(identifier: "exportKeyFromReadView") - if let vc = vc as? ImportKeyOverviewController { - //vc.wasPushed = true - } root.isToolbarHidden = false root.pushViewController(vc, animated: true) } diff --git a/enzevalos_iphone/SwiftUI/Read/Tabbed Views/MessageViewChildren/DialogView.swift b/enzevalos_iphone/SwiftUI/SupportingViews/DialogView.swift similarity index 97% rename from enzevalos_iphone/SwiftUI/Read/Tabbed Views/MessageViewChildren/DialogView.swift rename to enzevalos_iphone/SwiftUI/SupportingViews/DialogView.swift index b4dafc54acee1fd5c30676d33adae31fed3f1a5c..2c93e4ed5edb0c28ce688bb6d4a62fe2d1a1c03a 100644 --- a/enzevalos_iphone/SwiftUI/Read/Tabbed Views/MessageViewChildren/DialogView.swift +++ b/enzevalos_iphone/SwiftUI/SupportingViews/DialogView.swift @@ -8,7 +8,7 @@ import SwiftUI -struct DialogView: View { +struct DialogView : View { let option: DialogOption var ctaAction: (() -> Void)? @@ -93,8 +93,8 @@ struct DialogView: View { } } - -struct WarningMessageView_Previews: PreviewProvider { +/* +struct DialogView_Previews: PreviewProvider { static var previews: some View { VStack{ DialogView(option: .postcard, ctaAction: action, additionalAction: action, dismissAction: action) @@ -107,6 +107,7 @@ struct WarningMessageView_Previews: PreviewProvider { print("Button click!") } } + */ extension View { public func addBorder<S>(_ content: S, width: CGFloat = 1, cornerRadius: CGFloat) -> some View where S : ShapeStyle { diff --git a/enzevalos_iphone/en.lproj/Localizable.strings b/enzevalos_iphone/en.lproj/Localizable.strings index 95fd261c43cfd9487d1475cc1d8d47047c2fcb5e..4d30a330e70c6058591c23d1f87078dad457b9cd 100644 --- a/enzevalos_iphone/en.lproj/Localizable.strings +++ b/enzevalos_iphone/en.lproj/Localizable.strings @@ -65,7 +65,6 @@ "KeyIsVerified" = "The Key is verified. It was verified on "; "KeyNotFound" = "No Key Found. This is an error, contact the developers!"; "Letter" = "Confidential and genuine mail"; - "Onboarding.headline"="Welcome"; "Onboarding.loginButtonText"="Login"; "Onboarding.loginButtonTextGoogle"="with"; @@ -82,7 +81,6 @@ "Onboarding.loginDescription" = "Enter your email address and password to log in."; "Onboarding.enterEmail"="Your email address:"; "Onboarding.enterPassword"="Your password:"; - //onbarding authenticationView "Your Password"="Your Password"; "Username"="Username"; @@ -123,7 +121,7 @@ "ReadView.Icon.Association.Multi" = "The person sent you %@ confidential mails before."; // NEW "ReadView.Icon.Secure.Expert" = "For experts: This mail was encrypted and signed."; "ReadView.Icon.Advice" = "Think twice when clicking on links."; -"ReceiveInsecureInfo" = "We do not know if the sender address represents the real sender. Be aware that a fraud can impersonate the sender. But you, the sender and all mail providers know the content of the mail."; +"ReceiveInsecureInfo" = "We do not know if the sender address represents the real sender. Be aware that a fraud can impersonate the sender. You, the sender and all mail providers know the content of the mail."; "ReadView.Icon.Insecure.Expert" = "For experts: This mail was neither encrypted nor signed."; "ReadView.Icon.Key" = "We know that the sender can send confidential mails."; "ReadView.Icon.NoKey" = "We do not know if the sender can send confidential mails."; @@ -365,5 +363,23 @@ "Feedback.Name" = "Feedback"; "MailView.MoreMails" = "See more mails"; "Searchbar.Title" = "Search"; - "Gamification.Overview.HeaderTitle" = "Badge Case"; +"Security.Dialog.Title.No.Crypto" = "Insecure"; +"Security.Dialog.Title.Enc+Sign" = "Confidential and genuine mail"; +"Security.Dialog.Title.UnableToDecrypt" = "Not readable mail"; +"Security.Dialog.Title.InvalidSignature" = "Corrupted mail"; +"Security.Dialog.Title.Plain+Sig" = "Genuine but not confidential mail"; +"Security.Dialog.Title.PlainMissingPublicKeyToVerify" = "Not confidential mail"; +"Security.Dialog.Title.Enc+NoSign" = "Confidential but not genuine mail"; +"Security.Dialog.Title.EncMissingPublicKeyToVerify" = "Confidential mail"; +"Security.Dialog.Body.EncButMissingPublicKeyToVerify" = "We can not verify the sender because sender's ID is missing. \nOnly you and the sender can read the content of the email."; +"Security.Dialog.Body.PlainMissingPublicKeyToVerify" = "We can not verify the sender because sender's ID is missing. \nYou, the sender and all involed email provider can read the content of the email."; +"Security.Dialog.Button.Title.MoreInfo" = "More information"; +"Security.Dialog.Button.Title.OK" = "OK"; +"Security.Dialog.Button.Title.Import.SK" = "Import own key"; +"Security.Dialog.Button.Title.Ask.Resend.ForPK" = "Ask to resend"; +"Security.Dialog.Button.Title.Ignore.Mail" = "Ignore mail"; +"Security.Dialog.Button.Title.Confirmation" = "Ask for confirmation"; +"Security.Dialog.Button.Title.Invite" = "Ask to use encryption"; +"Security.Dialog.Button.Title.Ask.ForPK" = "Ask to send ID"; +"Security.Dialog.Button.Title.Send.PK" = "Send own public key"; diff --git a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 10.xcdatamodel/contents b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 10.xcdatamodel/contents index 92675aef3d195c73ab3e53760230085a470d840f..2c4e7d87c565afedc1ef3c41383e6150640db286 100644 --- a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 10.xcdatamodel/contents +++ b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 10.xcdatamodel/contents @@ -169,7 +169,7 @@ <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="Attachment" positionX="-315" positionY="-36" width="128" height="208"/> <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="163"/> diff --git a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 11.xcdatamodel/contents b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 11.xcdatamodel/contents new file mode 100644 index 0000000000000000000000000000000000000000..92675aef3d195c73ab3e53760230085a470d840f --- /dev/null +++ b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 11.xcdatamodel/contents @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="16119" 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="firstMail" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail"/> + <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"/> + <relationship name="secondMail" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="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="163"/> + <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