Read email headers with flags - Mailcore & swift - ios

I am using mailCore to fetch emails from server. I am done with session setup and basic flow. Now i am stuck in a minor problem and i am unable to figure it out.
Here is code of fetching email headers
let folder: String = "Inbox"
let folderInfoFetch : MCOIMAPFolderInfoOperation = imapSession.folderInfoOperation(folder)
folderInfoFetch.start { (error, folderInfo) in
if (error == nil) {
var numberOfMessages : Int32 = Int32(30)
numberOfMessages -= 1
let request: MCOIMAPMessagesRequestKind = .headers
let messagesNumbers = MCOIndexSet.init(range: MCORange.init(location: UInt64(abs((folderInfo?.messageCount)!-numberOfMessages)) , length: UInt64(numberOfMessages)))
let fetch : MCOIMAPFetchMessagesOperation = self.imapSession.fetchMessagesByNumberOperation(withFolder: folder, requestKind: request, numbers: messagesNumbers)
fetch.start({ (error, fetchedMessages, vanishedMessages) in
if(error != nil)
{
print("Error downloading message headers: \(String(describing: error))")
} else {
if let mails = fetchedMessages as? [MCOIMAPMessage] {
print(mails)
}
}
})
}
This code is successfully returning mail headers. But read/unread flags are missing in this. I have seen many solutions of this problem but they all in objective c.
i-e
MCOIMAPMessagesRequestKind requestKind = MCOIMAPMessagesRequestKindHeaders | MCOIMAPMessagesRequestKindFlags
It's solution is to send multiple types of flags in request. But how i can achieve this in swift ? Any help will be appreciated!
Solution Link : https://github.com/MailCore/mailcore2/issues/409

In swift4.2, I achieved this with the following syntax:
let requestKind: MCOIMAPMessagesRequestKind = [.headers, .flags]

So after banging my head the only solution which worked for me is append flags by taking its union.
let kind = MCOIMAPMessagesRequestKind()
let headers = kind.union(MCOIMAPMessagesRequestKind.headers)
let request = headers.union(MCOIMAPMessagesRequestKind.flags)
I know it's not best but it worked for me.

Related

JOSESwift jwe encryption failed to decode in the nimbus server

Had anybody used JOSESwift successfully? In my case, decryption in the server failing, probably cannot find the matching private key or wrong with encryption. Getting error 500.
My code is, getting the public keys from a server.
keys?.keys?.forEach({ (key) in
BPLogger.debug("\(key)")
do {
let jwkData = key.toJSONString()?.data(using: .utf8)
let rsaKey = try RSAPublicKey(data: jwkData!)
BPLogger.log("key components: \(rsaKey.parameters)")
BpidCache.shared.joseRsaKey = rsaKey
self?.generateParametersJose()
completion()
return
} catch {
BPLogger.debug("Error: \(error)")
}
})
The server expected a 'kid' field in the jose header, which was missing in the framework. So I have added it... The backend Java server uses nimbus library.
func generateParametersJose() {
let rsa = BpidCache.shared.joseRsaKey
var publicKey: SecKey? = nil
do {
publicKey = try rsa?.converted(to: SecKey.self)
} catch {
BPLogger.log("\(error)")
}
var header = JWEHeader(algorithm: .RSA1_5, encryptionAlgorithm: .A256CBCHS512)
// header.parameters["kid"] = "1"
let jwk = MidApi.Model.JWTKey(key: cek);
let jwkData = try! JSONEncoder().encode(jwk)
BPLogger.debug("jwkData = \(String(data: jwkData, encoding: .utf8)!)")
let payload = Payload(jwkData)
// Encrypter algorithms must match header algorithms.
guard let encrypter = Encrypter<SecKey>(keyEncryptionAlgorithm: .RSA1_5, encryptionKey: publicKey!, contentEncyptionAlgorithm: .A256CBCHS512) else {
return
}
guard let jwe = try? JWE(header: header, payload: payload, encrypter: encrypter) else {
BPLogger.error("Falied jwe creation.")
return
}
var comps = jwe.compactSerializedString.components(separatedBy: ".")
var jweHeader = comps.first
let data = jweHeader?.base64URLDecode()
var orgH = try! JSONDecoder().decode(BPJweHeader.self, from: data!)
orgH.kid = "1"
let newJson = try! JSONEncoder().encode(orgH).base64URLEncodedString()
comps[0] = newJson
let newHeader = comps.joined(separator: ".")
BPLogger.log("jwe.compactSerializedString = \(newHeader)")
headers = ["X-Encrypted-Key": newHeader]
// headers = ["X-Encrypted-Key": jwe.compactSerializedString] // this also fails
}
What am I doing wrong?
The latest version of JOSESwift (1.3.0) contains a fix for the problem that prevented setting additional header parameters.
You can now set the additional header parameters listed in RFC-7516. Setting the "kid" parameter like you tried to do in your question works like this:
var header = JWEHeader(algorithm: .RSA1_5, encryptionAlgorithm: .A256CBCHS512)
header.kid = "1"
If you use the framework via CocoaPods, make sure to run pod repo update to make sure you install the latest version which contains the fix.

How to fetch the inbox from email agent in swift/objective c using mailcore framework or Any framework?

Step 1: Link (https://github.com/MailCore/MailCore2)
Step 2: I have added mailcore-framework in my project
Step 3: pod install for UICKeyChainStore in my project done
Step 4: Send mail successfully using MCOSMTPSession, MCOMessageBuilder.
Step 5: My problem is that I am not able to fetch using mailcore. Is there any other framework for fetching mail (inbox)?
Sorry for late answer. I have two apps in the App Store, both of them use MailCore2, so I can explain you a thing or two about that.
Of course you can fetch the emails with MailCore2, this is the code. If you have any other doubt with MailCore2 write about that, I will try to find the answer.
var imapsession:MCOIMAPSession = MCOIMAPSession()
func prepareImapSession()
{
// CONFIGURE THAT DEPENDING OF YOUR NEEDS
imapsession.hostname = imapHostname // String
imapsession.username = userName // String
imapsession.password = password // String
imapsession.port = portIMAP // UInt32 number
imapsession.authType = MCOAuthType.saslLogin
imapsession.connectionType = MCOConnectionType.TLS
}
func useImapWithUIDS()
{
// There is more than one option here, explore depending of your needs
let requestKind : MCOIMAPMessagesRequestKind = .headers
let folder : String = "INBOX"
// HERE ALSO EXPLORE DEPENDING OF YOUR NEEDS, RANGE IT IS THE RANGE OF THE UIDS THAT YOU WANT TO FETCH, I SUGGEST TO YOU TO CHANGE THE // NUMBER ONE IF YOU HAVE A LOWER BOUND TO FETCH EMAIL
let uids : MCOIndexSet = MCOIndexSet(range: MCORangeMake(1, UINT64_MAX))
let fetchOperation = imapsession.fetchMessagesOperation(withFolder: folder, requestKind: requestKind, uids: uids)
fetchOperation?.start
{ (err, msg, vanished) -> Void in
if (err != nil)
{
error = err
NSLog((err?.localizedDescription)!)
}
else
{
guard let msgs = msg as? [MCOIMAPMessage]
else
{
print("ERROR GETTING THE MAILS")
return
}
for i in 0..<msgs.count
{
// THE SUBJECT
let subject = msgs[i].header.subject
// THE uid for this email. The uid is unique for one email
let uid = msgs[i].uid
// The sequenceNumber like the nomber say it is the sequence for the emails in the INBOX from the first one // (sequenceNumber = 1) to the last one , it not represent always the same email. Because if you delete one email then //next one will get the sequence number of that email that was deleted
let sequenceNumber = msgs[i].sequenceNumber
}
}
}
// MARK: - EXTRACT THE CONTENT OF ONE EMAIL, IN THIS FUNCTION YOU NEED THE uid, THE UNIQUE NUMBER FOR ONE EMAIL
func useImapFetchContent(uidToFetch uid: UInt32)
{
let operation: MCOIMAPFetchContentOperation = imapsession.fetchMessageOperation(withFolder: "INBOX", uid: uid)
operation.start { (Error, data) in
if (Error != nil)
{
NSLog("ERROR")
return
}
let messageParser: MCOMessageParser = MCOMessageParser(data: data)
// IF YOU HAVE ATTACHMENTS USE THIS
let attachments = messageParser.attachments() as? [MCOAttachment]
// THEN YOU NEED THIS PROPERTIE, IN THIS EXAMPLE I TAKE THI FIRST, USE WHAT EVER YOU WANT
let attachData = attachments?.first?.data
// FOR THE MESSAGEPARSER YOU CAN EPLORE MORE THAN ONE OPTION TO OBTAIN THE TEXT
let msgPlainBody = messageParser.plainTextBodyRendering()
}
You can give https://github.com/snipsco/Postal a try. This is a framework which aims to provide simple access to common email providers.

aws dynamodb how to use object mapper with batch get in ios swift

Thanks in advance for any help. I am trying to get Batch items (Load multiple) items from one DynamoDb table using the AWS iOS SDK (Swift). I can load one item using the Block syntax, but I need to load 10 or more than that. I don't want to use 10 Block calls to load them individually. I tried to follow the attach stackoverflow Link (where the similar solution is given) but I am getting the following compiler error message. I come from Java background, hence could also be a syntax issue. Is it the right way to load multiple items? I don't want to use low level API. Any help, where I am going wrong. Thanks.
aws dynamodb how to use object mapper with batch get in ios
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
var tasksList = Array<AWSTask<AnyObject>>()
for i in 1...10 {
tasksList.append(dynamoDBObjectMapper.load(AWSCards.self, hashKey: "SH_"+String(i), rangeKey: nil))
}
AWSTask.init(forCompletionOfAllTasksWithResults: tasksList).continueWithBlock { (task) -> AnyObject? in
if let cards = task.result as? [AWSCards] {
print(cards.count)
}
else if let error = task.error {
print(error.localizedDescription)
}
return nil
}
Have a try with the following codes (Swift 4.1, Feb 9th, 2018):
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
var tasksList = Array<AWSTask<AnyObject>>()
for i in 1...10 {
tasksList.append(dynamoDBObjectMapper.load(AWSCards.self, hashKey: "SH_"+String(i), rangeKey: nil))
}
AWSTask<AnyObject>.init(forCompletionOfAllTasksWithResults: tasksList).continueWith { (task) -> Any? in
if let cards = task.result as? [AWSCards] {
print(cards.count)
}
else if let error = task.error {
print(error.localizedDescription)
}
return nil
}
Your question is "how to use the object mapper" but it might be more efficient for you to not use it.
However, there is a way to use it. See Niklas's answer here and here (he copy & pasted), but something about it strikes me as fishy. I want to make the assertion that it is not as fast as the built-in batch-get function, but I am unsure. I suspect that this does not complete the items in parallel, or at least not as efficiently as in BatchGetItem.
See the docs: "In order to minimize response latency, BatchGetItem retrieves items in parallel."
According to Yosuke, "Currently, AWSDynamoDBObjectMapper does not support the batch get item. You need to load one item at a time if you want to use the object mapper" as of 2016. This still seems to be the case. I am using a version a couple versions behind, but not too far behind. Someone check.
In conclusion, if you are loading one item at a time, you are likely missing out on the whole purpose of BatchGetItem (low latency).
Pulling from various sources, including John Davis's question here, I have tested and ran this BatchGetItem result. Here ya go.
import AWSDynamoDB
let primaryKeyToSortKeyDict : [String : String] = .... // Your stuff
var keys = [Any]()
for key in primaryKeyToSortKeyDict.keys {
let partitionKeyValue = AWSDynamoDBAttributeValue()
partitionKeyValue?.s = String(key)
let sortValue = AWSDynamoDBAttributeValue()
sortValue?.s = String(primaryKeyToSortKeyDict[key]!)
keys.append(["partitionKeyAttributeName": partitionKeyValue, "sortKeyAttributeName": sortValue])
}
let keysAndAttributesMap = AWSDynamoDBKeysAndAttributes()
keysAndAttributesMap?.keys = keys as? [[String : AWSDynamoDBAttributeValue]]
keysAndAttributesMap?.consistentRead = true
let tableMap = [table : keysAndAttributesMap]
let request = AWSDynamoDBBatchGetItemInput()
request?.requestItems = tableMap as? [String : AWSDynamoDBKeysAndAttributes]
request?.returnConsumedCapacity = AWSDynamoDBReturnConsumedCapacity.total
guard request != nil else {
print("Handle some error")
return
}
AWSDynamoDB.default().batchGetItem(request!) { (output, error) in
print("Here is the batchgetitem output")
if error == nil {
// do output stuff
} else {
// handle error
}
}

How to find out how many days have passed since an iOS app was opened the first time by a specific user/device?

I created an iOS app and would like to give the users the opportunity to use the app for free for 30 days since the app was installed and started the very first time.
After these 30 days I'd like to turn off most functionality. The only way to retain full functionality is to login in with username and password which the user can register for on a dedicated website.
My problem is:
When the app was started the very first time, I'd like store a unique identifier and a timestamp of this moment on a remote server. Then I'd know when 30 days have passed for this unique id.
Unfortunately, it seems like the only uuid-like identifier on iOS is identifierForVendor. According to the docs, when the user removes all apps from the same vendor of the app and then reinstalls them, the uuid changes. Since this uuid would not match the one I stored to the database on my remote server, the user would be able to re-use the app although 30 days had passed.
From the docs:
The uuid on iOS uses the identifierForVendor property. It is unique to
the device across the same vendor, but will be different for different
vendors and will change if all apps from the vendor are deleted and
then reinstalled. See the official Apple docs.
The UUID will be the same if app is restored from a backup or iCloud
as it is saved in preferences. Users using older versions of this
plugin will still receive the same previous UUID generated by another
means as it will be retrieved from preferences.
My question is:
Is there a way to find out how many days have passed since the user opened the app the very first time, even if he deletes/reinstalls all apps made by me?
Note: Since I am using Phonegap/Cordova, a Cordova-compatible solution would be welcome but a native Swift/Objective-C solution would be ok too.
Your app can store it's own unique ID (from a server or randomly generated perhaps) and/or a date-stamp in the iOS device's keychain. The keychain is not cleared when an app is deleted from a device.
See Apple's Keychain services programming guide.
What about NSUbiquitousKeyValueStore?
// Use `NSUbiquitousKeyValueStore`
let iCloudKeyStore: NSUbiquitousKeyValueStore? = NSUbiquitousKeyValueStore()
let isMyAppInstalled = true
iCloudKeyStore?.setBool(isMyAppInstalled, forKey: "isMyAppInstalled")
iCloudKeyStore?.synchronize()
If You can Create a webService to catch and Store Device ID's on a Sqlite table, here is the Solution:
You can get Device ID like this:
let DID = UIDevice.currentDevice().identifierForVendor!.UUIDString
print("Device ID is : \(DID)")
and then send this ID to your webSerivce like this:
on AppDelegate put this :
internal static var DeviceList:[StructID] = []
then Create a Swift Class with name StructID, like this :
import Foundation
class StructID{
internal var id:Int!
internal var DevieID:String!
internal var AllowInstall:String!
internal var InstalledTime:String!
internal var InstallCount:String!
}
then on Your FirstViewController You can send and store many data included how many this id installed this app or when is the first install or is this id alowed tho use this app? or anything you want !
let IDs = StructID()
IDs.DevieID = DID
IDs.AllowInstall = AllowInstall // <declare it yourself
IDs.InstalledTime = InstalledTime // <declare it yourself
IDs.InstallCount = InstallCount // <declare it yourself
var dictionary = Dictionary<String, AnyObject>()
dictionary["id"] = IDs.id
dictionary["DevieID"] = IDs.DevieID
dictionary["AllowInstall"] = IDs.AllowInstall
dictionary["InstalledTime"] = IDs.InstalledTime
dictionary["InstallCount"] = IDs.InstallCount
let url = NSURL(string: "http://www.YourWebAddress.com/service.php?action=insert")
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
request.timeoutInterval = NSTimeInterval(10)
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(dictionary, options: NSJSONWritingOptions(rawValue: 0))
request.HTTPBody = jsonData
} catch {
print("json data error")
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTaskWithRequest(request) { (NSData, response, error) -> Void in
let id = String (data: NSData!, encoding: NSUTF8StringEncoding)
DeviceList.id = Int(id!) // <= its id column of your table
AppDelegate.DeviceList.append(IDs) // <= it the Device id Your are sending
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.navigationController?.popToRootViewControllerAnimated(true)
})
}
task.resume()
}
and your php code on your webService:
function getConnection () {
$servername = "localhost";
$username = "YourDatabseName_db";
$password = "Password";
$dbname = "YourDatabseName_db";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
return $conn;
}
$action = $_GET["action"];
if ($action == "insert") {
$conn = getConnection();
$jsonString = file_get_contents('php://input');
$data = json_decode($jsonString, true);
$DeviceID = $data["DeviceID"];
$AllowInstall = $data["AllowInstall"];
$InstalledTime = $data["InstalledTime"];
$InstallCount = $data["InstallCount"];
$sql = "INSERT INTO `YourDatabase_db`.`YourTableName` (`DeviceID`, `AllowInstall`, `InstalledTime `, `InstallCount `) VALUES ('$DeviceID', '$AllowInstall', '$InstalledTime', '$InstallCount')";
if (mysqli_query($conn, $sql)){
$last_id = mysqli_insert_id($conn);
echo $last_id;
} else {
echo "Error: " . $sql . "<br>" . mysqli_error($conn);
}
finally When Your app Stars each time, you an Check all the Conditions and do what ever you want...
for readig Back from php put it on your webservice php :
if ($action == "selectgeneral") {
$conn = getConnection();
$sql = "SELECT * FROM yourtablename";
$result = $conn->query($sql);
$output = array();
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$record = array();
$record["id"] = $row["id"];
$record["DevieID"] = $row["DevieID"];
$record["AllowInstall"] = $row["AllowInstall"];
$record["InstalledTime"] = $row["InstalledTime"];
$record["InstallCount"] = $row["InstallCount"];
array_push($output, $record);
}
} else {
echo "0 results";
}
$conn->close();
echo json_encode($output);
and if you want to read your data from your php :
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) {(NSData, response, error) -> Void in
do {
let records = try NSJSONSerialization.JSONObjectWithData(NSData!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
for record in records {
let record = StructID()
record.id = Int(record["id"] as! String)
record.DevieID = record["DevieID"] as! String
record. AllowInstall = record["AllowInstall"] as! String
record.InstalledTime = Int(record["InstalledTime"] as! String)
record.InstallCount = Int(record["InstallCount"] as! String)
AppDelegate.DeviceID.append(record)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableViewNote.reloadData()
})
}
catch {
print("Json Error")
}
}
task.resume()
Sorry if its a little complicated.

Mailcore 2 iOS How to retrieve Reply email address

Mailcore 2 integration:
I am working on "Reply", "Reply All" , "Forward" and Move To features. When I started up with "Reply", I could able to get the Reply mail body full message from MCOMessageView class -> content = [(MCOIMAPMessage *) _message htmlRenderingWithFolder:_folder delegate:self]; and displayed in reply compose view.
Now, I need pre-populate "Reply" (Recipient To) email in the compose view. When I tried "_message.header.replyTo", it is giving me like "mailcore::Address:0x17df0c70 getsy sandriya getsypri8796#gmail.com"
But I need to retrieve the mail address(es) alone from this "_message.header.replyTo", it is giving me like "mailcore::Address:0x17df0c70 getsy sandriya getsypri8796#gmail.com"
How to retrieve Reply email address from "mailcore:.........", please advise.
The definitive answer for me is like the following, i made a quick Swift v2.3 example and it run like the following :
let imapsession = MCOIMAPSession()
imapsession.hostname = "YOUR SERVER"
imapsession.port = 993
imapsession.username = "EMAIL"
imapsession.password = "PASS"
imapsession.connectionType = MCOConnectionType.TLS
let requestKind : MCOIMAPMessagesRequestKind = MCOIMAPMessagesRequestKind.Headers
let folder : String = "INBOX"
let uids : MCOIndexSet = MCOIndexSet(range: MCORangeMake(1, UINT64_MAX))
let fetchOperation : MCOIMAPFetchMessagesOperation = imapsession.fetchMessagesOperationWithFolder(folder, requestKind: requestKind, uids: uids)
fetchOperation.start { (err, msg, vanished) -> Void in
print("error from server \(err)")
print("fetched \(msg?[0]) messages")
let msgs = msg
if msgs?.count > 0 {
for m in msgs! {
let MSG: MCOIMAPMessage = m as! MCOIMAPMessage
let op = imapsession.fetchMessageByUIDOperationWithFolder(folder, uid: MSG.uid)
op.start { (err, data) -> Void in
let msgParser = MCOMessageParser(data: data)
let html: String = msgParser.plainTextBodyRendering()
print("*********************")
print("Mail body\(html)")
print("mail sender:\(MSG.header.sender)")
print("mail recipient:\(MSG.header.to)")
print("mail subject:\(MSG.header.subject)")
print("*********************")
}
}
}
}
In the example above i went a bit far and i gather the mail content as text. you can do it in HTML also.
Hope this answer will help you and help other people who fall in the same issue !

Resources