Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • enzevalos/enzevalos_iphone
1 result
Show changes
Commits on Source (6)
Showing
with 657 additions and 270 deletions
......@@ -41,3 +41,5 @@ DerivedData
DerivedData.noindex
*.xcuserstate
*.swp
private
*.zip
......@@ -13,6 +13,7 @@
0E1C457D24055F87006D104A /* signencinlineK9.eml in Resources */ = {isa = PBXBuildFile; fileRef = 0E1C457924055F87006D104A /* signencinlineK9.eml */; };
0E1C458024055FB7006D104A /* Alice.v3.pubsec.asc in Resources */ = {isa = PBXBuildFile; fileRef = 0E1C457E24055FB7006D104A /* Alice.v3.pubsec.asc */; };
0E1C458124055FB7006D104A /* Alice.v3.pub.asc in Resources */ = {isa = PBXBuildFile; fileRef = 0E1C457F24055FB7006D104A /* Alice.v3.pub.asc */; };
0E6551A72406A42300DE30FC /* SearchHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6551A62406A42300DE30FC /* SearchHelper.swift */; };
3E6B07DE2011246500E49609 /* invitationText.html in Resources */ = {isa = PBXBuildFile; fileRef = 3E6B07DD2011246500E49609 /* invitationText.html */; };
3EB4FA9F2012007C001D0625 /* DialogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EB4FA9D2012007C001D0625 /* DialogViewController.swift */; };
3EB4FAA12012007C001D0625 /* Dialog.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3EB4FA9E2012007C001D0625 /* Dialog.storyboard */; };
......@@ -270,6 +271,7 @@
0E1C457924055F87006D104A /* signencinlineK9.eml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = signencinlineK9.eml; sourceTree = "<group>"; };
0E1C457E24055FB7006D104A /* Alice.v3.pubsec.asc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Alice.v3.pubsec.asc; sourceTree = "<group>"; };
0E1C457F24055FB7006D104A /* Alice.v3.pub.asc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Alice.v3.pub.asc; sourceTree = "<group>"; };
0E6551A62406A42300DE30FC /* SearchHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHelper.swift; sourceTree = "<group>"; };
1D4A9E60565DECF52C011BC0 /* Pods-enzevalos_iphone-AdHoc.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphone-AdHoc.release.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphone-AdHoc/Pods-enzevalos_iphone-AdHoc.release.xcconfig"; sourceTree = "<group>"; };
3E6B07DD2011246500E49609 /* invitationText.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = invitationText.html; path = Invitation/invitationText.html; sourceTree = "<group>"; };
3E9708AD1FAC925D005825C9 /* enzevalos_iphone.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = enzevalos_iphone.entitlements; sourceTree = "<group>"; };
......@@ -1149,6 +1151,7 @@
A1EB05971D956947008659C1 /* InboxViewController.swift */,
F120607F1DA540FE00F6EF37 /* RefreshControlExtension.swift */,
A1F9923B1DA7DD2E0073BF1B /* InboxTableViewCell.xib */,
0E6551A62406A42300DE30FC /* SearchHelper.swift */,
);
name = inbox;
sourceTree = "<group>";
......@@ -1642,6 +1645,7 @@
472F39701E14F75C009260FB /* DataHandler.swift in Sources */,
A1C62E9A2018F716000E5273 /* OnboardingValueState.swift in Sources */,
A1EB05901D956923008659C1 /* ReadViewController.swift in Sources */,
0E6551A72406A42300DE30FC /* SearchHelper.swift in Sources */,
472F398E1E251B8D009260FB /* MailAddress.swift in Sources */,
A198D2292056B384004CC838 /* SendViewDelegate.swift in Sources */,
479011492289975D0057AB04 /* NoSecIconStyleKit.swift in Sources */,
......
......@@ -1084,6 +1084,14 @@ class DataHandler {
return result
}
// filter all presistent mails without public key
func getAllNoPublicKeyPersistentMail() -> [PersistentMail] {
let result = getAllPersistentMails().filter({ $0.sigState == SignatureState.NoPublicKey })
return result
}
func getAllNotSignedPersistentMail() -> [PersistentMail] {
let result = getAllPersistentMails().filter({ $0.sigState == SignatureState.NoPublicKey })
return result
......
......@@ -36,7 +36,11 @@ fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
}
}
/**
EnzevalosContact is used for persons and items of the address book (by apple).
One enzevalosContact maps to one person/item of the address book.
One enzevalosContact links to multiple key records since the person have multiple keys.
*/
@objc(EnzevalosContact)
open class EnzevalosContact: NSManagedObject, Contact, Comparable {
......@@ -57,6 +61,9 @@ open class EnzevalosContact: NSManagedObject, Contact, Comparable {
}
}
/**
Just like nameOptional or a localized variant of "no name". Just in case you like to enforce a name.
*/
open var name: String {
if let name = nameOptional {
return name
......@@ -146,6 +153,9 @@ open class EnzevalosContact: NSManagedObject, Contact, Comparable {
}
}
/**
A set of associated public keys
*/
var publicKeys: Set<PersistentKey> {
get {
var pks = Set<PersistentKey>()
......@@ -155,7 +165,9 @@ open class EnzevalosContact: NSManagedObject, Contact, Comparable {
return pks
}
}
/**
A set of associated key records
*/
open var records: [KeyRecord] {
get {
if let krecords = self.keyrecords as? Set<KeyRecord> {
......@@ -195,6 +207,9 @@ open class EnzevalosContact: NSManagedObject, Contact, Comparable {
return date
}
/**
Related item in the address book (from apple)
*/
open var cnContact: CNContact? {
get {
if let cn = cnidentifier {
......@@ -207,6 +222,9 @@ open class EnzevalosContact: NSManagedObject, Contact, Comparable {
}
}
/**
Create a new cnContact in the address book.
*/
open var newCnContact: CNContact {
let con = CNMutableContact()
let name = self.displayname
......@@ -241,13 +259,6 @@ open class EnzevalosContact: NSManagedObject, Contact, Comparable {
return nil
}
func getAddressByMCOAddress(_ mcoaddress: MCOAddress) -> Mail_Address? {
if (mcoaddress.mailbox) != nil {
return getAddress(mcoaddress.mailbox.lowercased())
}
return nil
}
open func getMailAddresses() -> [MailAddress] {
var adr = [MailAddress] ()
for a in addresses {
......@@ -257,6 +268,9 @@ open class EnzevalosContact: NSManagedObject, Contact, Comparable {
return adr
}
/**
Is the address associated to the person(enzevalos contact)
*/
func isAddress(mailadr: String) -> Bool {
for adr in getMailAddresses() {
if mailadr.lowercased() == adr.mailAddress.lowercased() {
......
......@@ -29,6 +29,9 @@ extension EnzevalosContact {
}
@NSManaged public var displayname: String?
/**
Identifier to look up contact in the address book
*/
@NSManaged public var cnidentifier: String?
@NSManaged public var color: UIColor?
@NSManaged public var addresses: NSSet
......
......@@ -41,6 +41,9 @@ public class Folder: NSManagedObject {
}
}
/**
Please read the RFC.
*/
var uids: MCOIndexSet {
get {
......
......@@ -22,9 +22,15 @@ extension Folder {
@NSManaged public var keyRecords: NSSet?
@NSManaged public var path: String //backendFolderPath
@NSManaged public var lastUpdate: Date?
/**
Used for logging.
*/
@NSManaged public var pseudonym: String
@NSManaged public var icon: String?
/**
Please read the RFC!
*/
public var uidvalidity: UInt32? {
set {
if let num = newValue {
......
......@@ -39,6 +39,11 @@ class InboxViewController: UITableViewController, InboxCellDelegator {
}
}
}
// how many seconds should the app wait before processing search bar input
private let searchDelay = 0.5
// holds the currently active search timer (if it exists) so it can be terminated if a new character is typed before the execution
private var searchBarTimer: Timer?
@IBOutlet weak var lastUpdateButton: UIBarButtonItem!
var lastUpdateLabel = UILabel(frame: CGRect.zero)
......@@ -248,34 +253,60 @@ class InboxViewController: UITableViewController, InboxCellDelegator {
return searchController.isActive && !searchBarIsEmpty
}
func filterContentForSearchText(_ searchText: String, scope: Int = 0) {
var records = [KeyRecord]()
if scope == 0 || scope == 3 {
records += folder.records.filter({ (record: KeyRecord) -> Bool in
return record.name.lowercased().contains(searchText.lowercased())
})
}
if scope == 1 || scope == 3 {
records += folder.records.filter({ (record: KeyRecord) -> Bool in
let mails = record.inboxMails
return mails.filter({ (mail: PersistentMail) -> Bool in
mail.subject?.lowercased().contains(searchText.lowercased()) ?? false
}).count > 0
})
/**
searches a given string with a delay so that the entire client doesn't lag if the inbox is very big
- parameters:
- searchText: srtring to look for
- scope: where to search (look at _filterContentForSearchText)
*/
func startSearch(searchText: String, scope: Int = 0) {
// check if a search has been scheduled already and stop it to prevent multiple searches
if let searchBarTimer: Timer = self.searchBarTimer {
searchBarTimer.invalidate()
}
if scope == 2 || scope == 3 {
records += folder.records.filter({ (record: KeyRecord) -> Bool in
let mails = record.inboxMails
return mails.filter({ (mail: PersistentMail) -> Bool in
self.searchBarTimer = Timer.scheduledTimer(withTimeInterval: searchDelay, repeats: false, block: { _ in
self._filterContentForSearchText(searchText, scope: scope)
})
}
/**
Filters emails by a user input string and scope
- parameters:
- scope: 0 = contact name; 1 = subject; 2 = body; 3 = all
- searchText: the string we are looking for
*/
private func _filterContentForSearchText(_ searchText: String, scope: Int = 0) {
let records = folder.records.filter({ (record: KeyRecord) -> Bool in
if scope == 0
{
return containsSearchTerms(content: record.name, searchText: searchText)
}
let mails = record.inboxMails
return mails.filter({ (mail: PersistentMail) -> Bool in
// concatenate all strings that fall in our scope
var str = ""
if scope == 1 || scope == 3
{
str.append(contentsOf: mail.subject ?? "")
}
if scope == 2 || scope == 3
{
if let decryptedBody = mail.decryptedBody {
return decryptedBody.lowercased().contains(searchText.lowercased())
str.append(decryptedBody)
} else if !mail.isEncrypted {
return mail.body?.lowercased().contains(searchText.lowercased()) ?? false
str.append(contentsOf: mail.body ?? "")
}
return false
}
if scope == 3
{
str.append(contentsOf: record.name)
}
return containsSearchTerms(content: str, searchText: searchText)
}).count > 0
})
}
})
filteredRecords = records.unique.sorted()
tableView.reloadData()
......@@ -286,13 +317,13 @@ extension InboxViewController: UISearchResultsUpdating {
// https://www.raywenderlich.com/157864/uisearchcontroller-tutorial-getting-started
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!, scope: searchController.searchBar.selectedScopeButtonIndex)
startSearch(searchText: searchController.searchBar.text!, scope: searchController.searchBar.selectedScopeButtonIndex)
}
}
extension InboxViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: selectedScope)
startSearch(searchText: searchBar.text!, scope: selectedScope)
}
}
......
......@@ -23,12 +23,17 @@ import CoreData
import Contacts
import UIKit
/**
Key records are mostly used in the inbox and for id (views). For each key we have a single key record. There are also key records for each mail address without keys, for instance when receiving unsigned mails.
One person can have multiple key records, e.g. one with key, one without a key or multiple keys.
*/
@objc(KeyRecord)
public class KeyRecord: NSManagedObject, Record {
public var isUser: Bool {
get{
/*
/**
A keyrecord matches to the user if:
* we know the secret key, because we are the key owner
* the mail addresses matches
......@@ -45,6 +50,9 @@ public class KeyRecord: NSManagedObject, Record {
}
}
/**
Does the key record has a secret key?
*/
public var isSecretKey: Bool {
if let id = self.keyID {
return DataHandler.handler.findSecretKey(keyID: id) != nil
......@@ -86,6 +94,11 @@ public class KeyRecord: NSManagedObject, Record {
return nil
}
/**
MyNick is unique where as multiple key records can have the same name.
It is used to display different key records.
*/
public var myNick: String {
if let n = self.nick {
return n
......@@ -109,6 +122,10 @@ public class KeyRecord: NSManagedObject, Record {
return id
}
/**
One person can have multiple key records, e.g. one with key, one without a key or multiple keys.
In case of multiple keys we have a primaryID. The key of this id is used for decryption etc.
*/
public var isPrimaryID: Bool {
if let keyID = self.keyID {
for addr in self.addresses {
......@@ -129,6 +146,9 @@ public class KeyRecord: NSManagedObject, Record {
}
}
/**
It is the current fingerprint of the public key.
*/
public var fingerprint: String? {
get {
if let k = pgpKey {
......@@ -154,12 +174,19 @@ public class KeyRecord: NSManagedObject, Record {
}
}
/**
EnzevalosContact provides some contact information about the key record.
*/
public var ezContact: EnzevalosContact {
get {
return contact
}
}
/**
Is a list of all received mails from this record.
*/
public var mails: [PersistentMail] {
get {
if let m = persistentMails as? Set<PersistentMail> {
......@@ -168,7 +195,10 @@ public class KeyRecord: NSManagedObject, Record {
return []
}
}
/**
cnContact is the original corresponding contact in the address book.
*/
public var cnContact: CNContact? {
get {
return contact.cnContact
......@@ -195,6 +225,9 @@ public class KeyRecord: NSManagedObject, Record {
}
}
/**
Corresponding mail addresses to the current key.
*/
public var addresses: [MailAddress] {
get {
if let k = activeKey {
......
......@@ -68,6 +68,9 @@ class ListViewController: UITableViewController {
}
}
var loading = false
private let searchDelay = 0.5
private var searchTimer: Timer?
override func viewWillAppear(_ animated: Bool) {
tableView.reloadData()
......@@ -105,63 +108,53 @@ class ListViewController: UITableViewController {
deinit {
print("===============|| ListViewController deinitialized ||===============")
}
func filterContentForSearchText(_ searchText: String, scope: Int = 0) {
func startSearch(searchText: String, scope: Int = 0) {
// check if a search has been scheduled already and stop it to prevent multiple searches
if let searchBarTimer: Timer = self.searchTimer {
searchBarTimer.invalidate()
}
self.searchTimer = Timer.scheduledTimer(withTimeInterval: searchDelay, repeats: false, block: { _ in
self.filterContentForSearchText(searchText, scope: scope)
})
}
/**
- parameters:
- searchText: user input string to look for
- scope : 0 = subject: 1 = body: 2 = cc + to: 3 = all
*/
private func filterContentForSearchText(_ searchText: String, scope: Int = 0) {
filteredMails = contact!.mails.filter { mail in
var returnValue = false
switch scope {
case 0:
if let subject = mail.subject {
returnValue = subject.lowercased().contains(searchText.lowercased())
}
case 1:
if !returnValue && mail.decryptedBody != nil {
returnValue = mail.decryptedBody!.lowercased().contains(searchText.lowercased())
} else if !returnValue && mail.body != nil {
returnValue = mail.body!.lowercased().contains(searchText.lowercased())
}
case 2:
if !returnValue && mail.cc?.count > 0 {
if let result = mail.cc?.contains(where: { cc -> Bool in
if let mail = cc as? MailAddress {
return mail.mailAddress.contains(searchText.lowercased())
}
return false
}) {
returnValue = result
}
}
if !returnValue && mail.getReceivers().count > 1 {
returnValue = mail.getReceivers().contains(where: { rec -> Bool in
return rec.mailAddress.contains(searchText.lowercased())
})
}
default:
if let subject = mail.subject {
returnValue = subject.lowercased().contains(searchText.lowercased())
}
if !returnValue && mail.decryptedBody != nil {
returnValue = mail.decryptedBody!.lowercased().contains(searchText.lowercased())
} else if !returnValue && mail.body != nil && !mail.isEncrypted {
returnValue = mail.body!.lowercased().contains(searchText.lowercased())
}
if !returnValue && mail.cc?.count > 0 {
if let res = mail.cc?.contains(where: { cc -> Bool in
if let mail = cc as? MailAddress {
return mail.mailAddress.contains(searchText.lowercased())
}
return false
}) {
returnValue = res
}
}
if !returnValue && mail.getReceivers().count > 1 {
returnValue = mail.getReceivers().contains(where: { rec -> Bool in
return rec.mailAddress.contains(searchText.lowercased())
})
// a big string that we search through, contains everything from the scopes we are searching in
var str = ""
if scope == 0 || scope == 3 {
str.append(mail.subject ?? "")
}
if scope == 1 || scope == 3 {
if let decryptedBody = mail.decryptedBody {
str.append(decryptedBody)
} else if let body = mail.body {
str.append(body)
}
}
return returnValue
if scope == 2 || scope == 3 {
var receivers: [MailAddress] = []
receivers.append(contentsOf: mail.getReceivers())
receivers.append(contentsOf: mail.getCCs())
// build a string of all email addresses to input in the search function
receivers.map({ addr -> String in
return addr.mailAddress
}).forEach({ addr in
str.append(addr)
})
}
return containsSearchTerms(content: str, searchText: searchText)
}
tableView.reloadData()
......@@ -259,13 +252,12 @@ class ListViewController: UITableViewController {
extension ListViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let _ = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: searchBar.selectedScopeButtonIndex)
startSearch(searchText: searchController.searchBar.text!, scope: searchBar.selectedScopeButtonIndex)
}
}
extension ListViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: selectedScope)
startSearch(searchText: searchBar.text!, scope: selectedScope)
}
}
......@@ -25,7 +25,9 @@ import Contacts
@objc(Mail_Address)
open class Mail_Address: NSManagedObject, MailAddress {
/**
What key should be used to send encrypted messages or to verify messages?
*/
public var primaryKey: PersistentKey? {
get {
if hasKey {
......@@ -44,6 +46,9 @@ open class Mail_Address: NSManagedObject, MailAddress {
return address.lowercased()
}
/**
In the address book a mail address has a label (e.g. work, private, ...).
*/
open var label: CNLabeledValue<NSString> {
if let cnc = self.contact?.cnContact {
for adr in cnc.emailAddresses {
......@@ -62,6 +67,9 @@ open class Mail_Address: NSManagedObject, MailAddress {
return false
}
/**
Is this a mail address of the user?
*/
public var isUser: Bool {
get {
if let userAddr = UserManager.loadUserValue(Attribute.userAddr) as? String {
......@@ -71,6 +79,9 @@ open class Mail_Address: NSManagedObject, MailAddress {
}
}
/**
Just create a MCOAddress. Mailcore (our IMAP/SMTP library) needs MCOAddresses to send mails etc.
*/
public func transform () -> MCOAddress {
var name = address
if let contact = contact {
......
......@@ -34,9 +34,18 @@ extension Mail_Address {
@NSManaged public var cc: NSSet?
@NSManaged public var from: NSSet?
@NSManaged public var to: NSSet?
/**
Just for logging.
*/
@NSManaged public var pseudonym: String
@NSManaged public var primaryKeyID: String? //TODO: set to private?
/**
What key should be used to send encrypted messages or to verify messages?
*/
@NSManaged public var primaryKeyID: String?
@NSManaged private var keys: NSSet?
/**
Did we invited the mail address owner to use encryption?
*/
@NSManaged public var invitations: Int16
var primaryActiveKeyID: String? {
......
......@@ -9,6 +9,13 @@
import Foundation
import CoreData
/**
Is a stored public key.
It stores the keydata, some additional information and relations to key records etc.
A key can be part of a key chain, such that one key introduced another key (yet not implemented)
*/
@objc(PersistentKey)
public class PersistentKey: NSManagedObject {
......@@ -44,6 +51,9 @@ public class PersistentKey: NSManagedObject {
return self.verifiedDate != nil
}
/**
Is the key expired (according to the crypto scheme)?
*/
func isExpired() -> Bool {
let pgp = SwiftPGP()
if let key = pgp.loadKey(id: self.keyID) {
......
......@@ -18,22 +18,62 @@ extension PersistentKey {
@NSManaged public var keyID: String
@NSManaged public var verifiedDate: Date?
/**
When did we received the last signed mail?
*/
@NSManaged public var lastSeen: Date?
/**
When did we received the last mail with autocrypt header?
*/
@NSManaged public var lastSeenAutocrypt: Date?
/**
When did we imported the key?
*/
@NSManaged public var discoveryDate: Date?
@NSManaged private var mailaddress: NSSet?
/**
What is the mail which contains this key (only the first)?
*/
@NSManaged public var firstMail: PersistentMail?
/**
A pseudonym was used for logging.
*/
@NSManaged public var pseudonym: String
/**
Did we send our own public key to the owner of this public key?
This means does the owner knows our public key?
*/
@NSManaged public var sentOwnPublicKey: Bool
@NSManaged public var signedMails: NSSet?
@NSManaged private var record: KeyRecord?
@NSManaged private var activeKey: PersistentKey?
/**
This is only for the travler scenario. You can ignore it.
*/
@NSManaged public var activeRepeal: PersistentMail?
/**
Currently not used.
*/
@NSManaged private var parentKey: PersistentKey?
/**
Currently not used.
*/
@NSManaged private var childKeys: NSSet?
/**
This is only for the travler scenario. You can ignore it.
*/
@NSManaged public var repealedByMails: NSSet?
/**
This is only for the travler scenario. You can ignore it.
*/
@NSManaged private var isRepealed: Bool
/**
This is only for the travler scenario. You can ignore it.
*/
@NSManaged private var gotFailedCallForUse: Bool
/**
This is only for the travler scenario. You can ignore it.
*/
@NSManaged private var isMisstrusted: Bool
var mailaddresses: NSSet? {
......@@ -64,6 +104,9 @@ extension PersistentKey {
return parentKey != nil
}
/**
Currently not used.
*/
var rootKey: PersistentKey {
get {
var key = self
......@@ -100,7 +143,9 @@ extension PersistentKey {
keys.append(rootKey)
return keys
}
/**
This is only for the travler scenario. You can ignore it.
*/
public var repealed: Bool {
get {
return rootKey.isRepealed
......@@ -109,7 +154,9 @@ extension PersistentKey {
rootKey.isRepealed = newRepealState
}
}
/**
This is only for the travler scenario. You can ignore it.
*/
public var failedCallForUse: Bool {
get {
return rootKey.gotFailedCallForUse
......@@ -118,7 +165,9 @@ extension PersistentKey {
rootKey.gotFailedCallForUse = newState
}
}
/**
This is only for the travler scenario. You can ignore it.
*/
public var misstrust: Bool {
get {
return rootKey.isMisstrusted
......@@ -127,7 +176,9 @@ extension PersistentKey {
rootKey.isMisstrusted = trust
}
}
/**
This is the autocrypt prefer enc state.
*/
public var prefer_encryption: EncState{
set {
let name = "preferEncryption"
......
......@@ -21,7 +21,16 @@
import Foundation
import CoreData
/**
A PersistentMail stores all information about one mail:
- Sender, receivers
- subject, date
- body
- encryption state
- signature state
- attached files, keys
*/
@objc(PersistentMail)
open class PersistentMail: NSManagedObject, Mail {
public var predecessor: PersistentMail? = nil
......@@ -66,6 +75,9 @@ open class PersistentMail: NSManagedObject, Mail {
}
}
/**
This the signed first mail of the signed key?
*/
var isNewPubKey: Bool {
guard let signedKey = self.signedKey else {
return false
......@@ -77,6 +89,9 @@ open class PersistentMail: NSManagedObject, Mail {
return false
}
/**
Just a flag if the user read this mail.
*/
var isRead: Bool {
get {
let value = flag.contains(MCOMessageFlag.seen)
......@@ -92,6 +107,9 @@ open class PersistentMail: NSManagedObject, Mail {
}
}
/**
Just a flag if the user answered the mail.
*/
var isAnwered: Bool {
get {
let value = flag.contains(MCOMessageFlag.answered)
......@@ -132,6 +150,10 @@ open class PersistentMail: NSManagedObject, Mail {
return returnString
}
/**
Just a short part of the body. Can be used for a preview.
In case of trouble there is no preview.
*/
var shortBodyString: String? {
guard !trouble else {
return nil
......
......@@ -21,7 +21,6 @@
import Foundation
import CoreData
extension PersistentMail {
@nonobjc open override class func fetchRequest() -> NSFetchRequest<NSFetchRequestResult> {
......@@ -29,11 +28,20 @@ extension PersistentMail {
}
@NSManaged public var body: String?
/**
Deprecated
*/
@NSManaged public var visibleBody: String?
@NSManaged public var decryptedBody: String?
@NSManaged public var encryptedBody: String?
@NSManaged public var date: Date
@NSManaged public var secretKey: String? // secret key which is in the mail (to import)
/**
secret key which is attacted to the mail (to import)
*/
@NSManaged public var secretKey: String?
/**
The corresponding key record
*/
@NSManaged public var record: KeyRecord?
public var hasAttachment: Bool {
......@@ -45,6 +53,9 @@ extension PersistentMail {
}
}
/**
Some mail flags, e.g. read flag, answered flag (according to RFC)
*/
public var flag: MCOMessageFlag {
set {
if newValue != flag {
......@@ -68,28 +79,86 @@ extension PersistentMail {
}
}
/**
Please use encryption state
*/
@NSManaged public var isEncrypted: Bool
/**
This mail is signed but check isSigned, too.
We recommand to use signature state
*/
@NSManaged public var isSigned: Bool
/**
We could verify the signature (with a given public key)
*/
@NSManaged public var isCorrectlySigned: Bool
/**
This mail is encrypted but we could not decrypt the message (e.g. the secret key is missing)
*/
@NSManaged public var unableToDecrypt: Bool
/**
Subject of a mail (according to RFC)
*/
@NSManaged public var subject: String?
/**
Which folder is used to store the mail?
*/
@NSManaged public var folder: Folder
@NSManaged public var firstKey: PersistentKey? // NOT USED
/**
Not used.
*/
@NSManaged public var firstKey: PersistentKey?
/**
What key is the signature key of this mail?
*/
@NSManaged public var signedKey: PersistentKey?
/**
Did we received the mail?
*/
@NSManaged public var received: Bool
/**
Only used in traveler scenario.
*/
@NSManaged public var deleteWhileTravel: Bool
/**
Only used in traveler scenario.
*/
@NSManaged public var storeEncrypted: Bool
/**
Only used in traveler scenario.
*/
@NSManaged public var repealsKey: PersistentKey?
/**
This can be used for threading. This is defined in a RFC.
*/
@NSManaged public var gmailMessageID: NSNumber?
/**
This can be used for threading. This is defined in a RFC.
*/
@NSManaged public var gmailThreadID: NSNumber?
/**
Please read up this mail in the RFC!
*/
@NSManaged public var messageID: String?
/**
Currently not used.
*/
@NSManaged public var notLoadedMessages: String?
@NSManaged public var decryptedKey: PersistentKey? // Only for traveler scenario
/**
Only for traveler scenario.
*/
@NSManaged public var decryptedKey: PersistentKey?
/**
Sender mail client.
*/
@NSManaged public var xMailer: String?
/**
Key id of the signature key.
TODO rename to signedKeyID
*/
public var keyID: String? {
set {
self.willChangeValue(forKey: "keyID")
......@@ -116,6 +185,9 @@ extension PersistentMail {
}
/**
Does this mail has some (crypto) errors, e.g. manipulated encryption or signature?
*/
public var trouble: Bool {
set {
self.willChangeValue(forKey: "trouble")
......@@ -130,6 +202,9 @@ extension PersistentMail {
}
}
/**
Please read the RFC!
*/
public var uid: UInt64 {
set {
self.willChangeValue(forKey: "uid")
......@@ -146,6 +221,9 @@ extension PersistentMail {
return 0
}
}
/**
Please read the RFC!
*/
public var uidvalidity: UInt32? {
set {
if let num = newValue {
......@@ -215,6 +293,9 @@ extension PersistentMail {
}
/**
Enter a password and if possible import the secret keys.
*/
public func processSecretKey(pw: String?) throws -> Bool {
var sk = ""
if let key = secretKey {
......@@ -232,6 +313,9 @@ extension PersistentMail {
@NSManaged public var cc: NSSet?
@NSManaged public var to: NSSet
@NSManaged public var attachments: NSSet?
/**
Can be used for threading. Please read the RFC.
*/
@NSManaged public var referenceMails: NSSet?
}
......
//
// SearchHelper.swift
// enzevalos_iphone
//
// Created by lazarog98 on 26.02.20.
// Copyright © 2020 fu-berlin. All rights reserved.
//
import Foundation
/**
A collection of helper methods that are used for the different search bars
*/
/**
Function to be used to find mails that contain the search terms. All terms (separated by spaces) need to be contained in the search text.
- parameters:
- content: The String that will be searched
- searchText: Search terms (space-separated) that will be searched for
*/
func containsSearchTerms ( content : String?, searchText: String) -> Bool
{
guard searchText.count > 0 else {
///Case empty search
return true
}
guard let content = content else {
//Case Mail has no body/subject
return false
}
var longterms : [String] = []
var terms : [String] = []
//Break String into substrings separated by quoatation marks
longterms = searchText.components(separatedBy: "\"")
var i = 0
//even elements will be outside the quotation marks and need to be separated again
while (i < longterms.count)
{
if i % 2 == 0
{
terms.append(contentsOf: longterms[i].lowercased().components(separatedBy: " "))
}
else
{
terms.append(longterms[i].lowercased())
}
i+=1
}
var found = true
for t in terms
{
if !(t == "")
{
found = found && content.lowercased().contains(t)
}
}
return found
}
......@@ -10,19 +10,39 @@
import Foundation
import CoreData
/**
*/
extension SecretKey {
@nonobjc public class func fetchRequest() -> NSFetchRequest<SecretKey> {
return NSFetchRequest<SecretKey>(entityName: "SecretKey")
}
/**
Did we exported the secretkey?
*/
@NSManaged public var exported: Bool
/**
When did we import the secretkey?
*/
@NSManaged public var importedDate: NSDate?
@NSManaged public var keyID: String
/**
Used in the travler scenario. Just ignore it.
*/
@NSManaged public var obsolete: Bool
/**
Not used yet. Should store how we imported the key (generated on device, imported from mail via autorcrypt, via iTunes, ...)
*/
@NSManaged public var origin: Int16
/**
Just added in case of multiple accounts.
*/
@NSManaged public var account: Account?
/**
A Set of mails where as the key was used for decryption.
*/
@NSManaged public var decryptedMails: NSSet?
}
......
......@@ -592,30 +592,34 @@ class SwiftPGP: Encryption {
return CryptoObject(chiphertext: data, plaintext: plaintext, decryptedData: plaindata, sigState: sigState, encState: encState, signKey: sigKeyID, encType: CryptoScheme.PGP, signedAdrs: signedAdr)
}
// A help function for findMailForSecrectKey and findNotSignedMailForPublicKey
func keyAsKeyList(keyID: String) -> [Key] {
var keyList = [Key]()
let key: Key = loadKey(id: keyID)!
keyList.append(key)
if let key: Key = loadKey(id: keyID) {
keyList.append(key)
}
return keyList
}
func findMailForKey(keyID: String) {
var plaindata: Data? = nil
// Finds undecrypted mails in persistent mails and and tries to decrypt with the incoming secret key
func findMailForSecretKey(keyID: String) {
var encState = EncryptionState.UnableToDecrypt
var plaindata: Data? = nil
let key: [Key] = keyAsKeyList(keyID: keyID)
let datahandler = DataHandler.handler
let mailList = datahandler.getAllNotDecryptedPersistentMail()
for mail in mailList {
guard let data = mail.body?.data(using: String.Encoding.utf8, allowLossyConversion: true)
else {
// Change data
guard let data = mail.body?.data(using: .utf8) else {
return
}
// try to decrypt mail
// Try to decrypt mail
(plaindata, encState) = decryptMessage(data: data, keys: key, encForCurrentSK: true)
if let plaindata = plaindata, encState == EncryptionState.ValidedEncryptedWithCurrentKey || encState == EncryptionState.ValidEncryptedWithOldKey {
// update database
mail.body = String.init(data: plaindata, encoding: .utf8)
// Update database
mail.isEncrypted = true
mail.unableToDecrypt = false
mail.body = String.init(data: plaindata, encoding: .utf8)
datahandler.save(during: "decryption of older mails")
}
}
......@@ -647,6 +651,28 @@ class SwiftPGP: Encryption {
return (nil, EncryptionState.NoEncryption)
}
// Finds unsigned mails in persistent mails and and tries to verify them with the incoming public key
func findNotSignedMailForPublicKey(keyID: String) {
var sigState = SignatureState.NoPublicKey
let key: [Key] = keyAsKeyList(keyID: keyID)
let datahandler = DataHandler.handler
let mailList = datahandler.getAllNotSignedPersistentMail()
for mail in mailList {
// Change data
guard let data = mail.body?.data(using: .utf8) else {
return
}
// Try to verify mail signature
sigState = verifySignature(data: data, attachedSignature: nil, keys: key)
if sigState == SignatureState.ValidSignature {
// Update database
mail.isSigned = true
mail.isCorrectlySigned = true
datahandler.save(during: "verifying of older mails")
}
}
}
private func verifySignature(sigString: String, attachedSignature: Data?, keys: [Key]) -> SignatureState {
if let unarmored = try? Armor.readArmored(sigString){
return verifySignature(data: unarmored, attachedSignature: attachedSignature, keys: keys)
......