Mailcore: Inexpensive way to get message summary - mailcore2

I know I can get a plain text version of a message using plainTextRenderingOperationWithMessage: but it's very time consuming. I'm only trying to put together a short message summary. Is there a way to get this from the MCOIMAPMessage/MCOAbstractPart without using plainTextRenderingOperationWithMessages:?
I've got a semi-working solution with the following code but it still seems slow (about 2 seconds for the operation to complete) considering all the data should be contained in MCOIMAPMessage which is fully downloaded. Is this as fast as it gets or am I missing something?
MCOIMAPMessage *m = [NSKeyedUnarchiver unarchiveObjectWithData:self.email.mcomessage];
MCOIMAPFetchContentOperation * op =
[session fetchMessageAttachmentByUIDOperationWithFolder:folder
uid:[m uid]
partID:#"1"
encoding:MCOEncoding8Bit];
[op start:^(NSError * error, NSData * partData) {
NSString *string = [[NSString alloc] initWithData:partData encoding:NSUTF8StringEncoding];
string = [string mco_strippedWhitespace];
if (string.length > 200) string = [string substringToIndex:200];
NSLog(#"String: %#", string);
}];

Related

JSON data has "bad" characters that causes NSJSONSerialization to die

I am using the ATV version of TVH Client - if you haven't looked at this it's worth looking at TVH to glimpse madness in the face. It has a JSON API that sends back data, including the electronic program guide. Sometimes the channels put accented characters in their data. Here is an example, this is the result from Postman, note the ? char in the description:
{
"eventId": 14277,
"episodeId": 14278,
"channelName": "49.3 CometTV",
"channelUuid": "02fe96403d58d53d71fde60649bf2b9a",
"channelNumber": "49.3",
"start": 1480266000,
"stop": 1480273200,
"title": "The Brain That Wouldn't Die",
"description": "Dr. Bill Cortner and his fianc�e, Jan Compton , are driving to his lab when they get into a horrible car accident. Compton is decapitated. But Cortner is not fazed by this seemingly insurmountable hurdle. His expertise is in transplants, and he is excited to perform the first head transplant. Keeping Compton's head alive in his lab, Cortner plans the groundbreaking yet unorthodox surgery. First, however, he needs a body."
},
If this data is fed into NSJSONSerialization, it returns an error. So to avoid this, the data is first fed into this function:
+ (NSDictionary*)convertFromJsonToObjectFixUtf8:(NSData*)responseData error:(__autoreleasing NSError**)error {
NSMutableData *FileData = [NSMutableData dataWithLength:[responseData length]];
for (int i = 0; i < [responseData length]; ++i) {
char *a = &((char*)[responseData bytes])[i];
if ( (int)*a >0 && (int)*a < 0x20 ) {
((char*)[FileData mutableBytes])[i] = 0x20;
} else {
((char*)[FileData mutableBytes])[i] = ((char*)[responseData bytes])[i];
}
}
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:FileData //1
options:kNilOptions
error:error];
if( *error ) {
NSLog(#"[JSON Error (2nd)] output - %#", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
NSDictionary *userInfo = #{ NSLocalizedDescriptionKey:[NSString stringWithFormat:NSLocalizedString(#"Tvheadend returned malformed JSON - check your Tvheadend's Character Set for each mux and choose the correct one!", nil)] };
*error = [[NSError alloc] initWithDomain:#"Not ready" code:NSURLErrorBadServerResponse userInfo:userInfo];
return nil;
}
return json;
}
This cleans up the case when there is a control character in the data, but not an accent like the case above. When I feed in that data I get the "Tvheadend returned malformed JSON" error.
One problem is that the user can change the character set among a limited number of selections, and the server does not tell the client what it is. So one channel might use UTF8 and another ISO-8891-1, and there is no way to know which to use on the client side.
So: can anyone offer a suggestion on how to process this data so we feed clean strings into NSJSONSerialization?
I still do not know the root cause of the problem I am seeing - the server is sending not only high-bit characters like the ones I noted above, but I also found that it contained control characters too! Looking over other threads it appears I am not the only one seeing this problem, so hopefully others will find this useful...
The basic trick is to convert the original data from the server to a string using UTF8. If there are any of these "bad" chars in it, the conversion will fail. So you check if the resulting string is empty, and try another charset. Eventually you'll get data back. Now you take that string and strip out any control chars. Now you take that result, which is now UTF8 "clean", and convert it back to UTF8 NSData. That will pass through the JSON conversion without error. Phew!
Here is the solution I finally used:
// ... the original data from the URL is in responseData
NSString *str = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
if ( str == nil ) {
str = [[NSString alloc] initWithData:responseData encoding:NSISOLatin1StringEncoding];
}
if ( str == nil ) {
str = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
}
NSCharacterSet *controls = [NSCharacterSet controlCharacterSet];
NSString *stripped = [[str componentsSeparatedByCharactersInSet:controls] componentsJoinedByString:#""];
NSData *data = [stripped dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
I hope someone finds this useful!

Web Service JSON nsdictionary unicode string [duplicate]

NSData* jsonData is the http response contains JSON data.
NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"jsonString: %#", jsonString);
I got the result:
{ "result": "\u8aaa" }
What is the proper way to encoding the data to the correct string, not unicode string like "\uxxxx"?
If you convert the JSON data
{ "result" : "\u8aaa" }
to a NSDictionary (e.g. using NSJSONSerialization) and print the dictionary
NSError *error;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
NSLog(#"%#", jsonDict);
then you will get the output
{
result = "\U8aaa";
}
The reason is that the description method of NSDictionary uses "\Unnnn" escape sequences
for all non-ASCII characters. But that is only for display in the console, the dictionary is correct!
If you print the value of the key
NSLog(#"%#", [jsonDict objectForKey:#"result"]);
then you will get the expected output
說
I don't quite understand what the problem is. AFNetworking has given you a valid JSON packet. If you want the above code to output the character instead of the \u… escape sequence, you should coax the server feeding you the result to change its output. But this shouldn't be necessary. What you most likely want to do next is run it through a JSON deserializer…
NSDictionary * data = [NSJSONSerialization JSONObjectWithData:jsonData …];
…and you should get the following dictionary back: #{#"result":#"說"}. Note that the result key holds a string with a single character, which I'm guessing is what you want.
BTW: In future, I suggest you copy-paste output into your question rather than transcribing it by hand. It'll avoid several needless rounds of corrections and confusion.

Translating C to Obj-C file reading code

I have this file which I need to read the first bytes to check the information.
I don't need to load the whole file, only the beginning..
The code in C is, more or less, what follows. It is a big code, so I just wrote the basic functionality here.
Now I want to make it 100% Objective-C, but I cannot find a way to do it properly.
FILE *f;
char *buf;
f = fopen ("/Users/foo/Desktop/theFile.fil", "rb");
if(f) {
fseek(f , 0 , SEEK_END);
size = ftell(f);
rewind (f);
buf = (char*) malloc (sizeof(char)*size);
switch( ntohl(*(uint32 *)buf) ) {
case 0x28BA61CE:
case 0x28BA4E50:
NSLog(#"Message");
break;
}
fclose(f);
free (buf);
The most close I got to this is what follows:
NSData *test = [[NSData alloc] initWithContentsOfFile:filePath];
This gets me all the binary, but anyway, I got stuck. Better try to start all over..
Any help appreciated!
Well, valid C code is valid Objective-C code. So this is already in Objective-C.
What's your actual goal? What are you trying to do with the file? Is there a reason you can't use NSData?
C code is already Obj-C. It's perfectly reasonable to just use what you're already doing. But if you're dead-set on using Obj-C objects to perform this, you want to take a look at NSInputStream.
The most close I got to this is what follows:
NSData *test = [[NSData alloc] initWithContentsOfFile:filePath];
This gets me all the binary, but anyway, I got stuck.
It's not clear where you're stuck, because that's the (simplest) correct way to read a file in one big slurp in Cocoa. You've successfully read the file; there's nothing more to do for that.
If you're looking to proceed to the switch statement, the pointer to the bytes that it read is [test bytes]. That's the pointer that you will want to assign to buf. See the NSData documentation.
Well.. I sorted that out.. And did what follows.
Cheers and thanks for the help!
NSString *filePath = [[NSString alloc] initWithFormat:#"/Users/foo/Desktop/theFile.fil"];
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSUInteger len = [data length];
Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, [data bytes], len);
NSNumber *size = [[NSNumber alloc] initWithUnsignedLong:len/2^20];
NSLog(#"%#", size);
switch( ntohl(*(uint32 *)byteData) ) {
case 0x28BA61CE:
case 0x28BA4E50:
NSLog(#"Message");
break;
}
[size release];
[filePath release];

dataWithContentsOfURL - What is expected from the server?

I am trying to create NSData with the contents of an URL:
NSString *theUrl = [NSString stringWithString:#"http://127.0.0.1:8090"];
NSError *connectionError = nil;
NSData *inData = [NSData dataWithContentsOfURL:[NSURL URLWithString:theUrl] options:NSDataReadingUncached error:&connectionError];
NSInteger code = [connectionError code];
if (code != 0)
{
NSString *locDesc = [NSString stringWithString:[connectionError localizedDescription]];
NSString *locFail = [NSString stringWithString:[connectionError localizedFailureReason]];
NSLog(#"Error: %d %# %#", code, locDesc, locFail);
}
else if ([inData length] == 0)
{
NSLog(#"No data");
}
I have a super simple Java http server running on the local host that returns Hello World to a client:
DataOutputStream os = new DataOutputStream(s.getOutputStream()); // s is the socket
os.writeBytes(new String("Hello World\0"));
os.flush();
os.close();
s.close();
When pointing Google Chrome to http://127.0.0.1:8090 it displays Hello World as expected so data is sent back. When I run the objective-c code the inData is empty (0x0, data length is 0), and the error code is 0 so I don't have an error to inspect. If I change theUrl to "http://www.google.com" it seems works fine as the data length becomes > 0.
So my question is why inData is empty when I go the to local http-server. Does the stream have to be terminated with a specific data sequence?
Is the server outputting an HTTP status code like it's supposed to? If the response doesn't contain a 200 status indicating that the request was completed successfully, that might be causing dataWithContentsOfURL:options:error: to fail.
A little more context would be useful, but a wild guess is that your "super simple" HTTP server does not send any headers or not the ones expected by NSURL.
Have you tried curl -i http://127.0.0.1:8090 to see what the output actually looks like?

Does core data do its own type casting in the background?

I am working on a simple comparison of two lists to see which items in an "evaluation" list are contained in a larger "target" list. I am getting the data on-the-fly- by parsing two CSV files and storing everything as strings. I successfully import the data into the data store and I can get a list of entities no problem
The problem comes when I actually do a search. Essentially, I am looking for short ISBNs in the form of 1234 from the evaluation list in the target list, which are in the form of 1234-5. The predicate I am using is I am using the CONTAINS comparison in the form of [NSString stringWithFormat:#"%# CONTAINS %#", kOC_Target_PrintBookCode, evalIsbn]
The error I get is the following (grabbed by my NSLog)
NSInvalidArgumentException: Can't look for value (1494) in string (49885); value is not a string
I get the impression that even though the ISBN is being read from a NSString and the Core Data store has the data point spec'd as a String, that Core Data is still doing something in the background with the value for whatever reason it sees fit. Any ideas?
Here is the relevant process logic (though I use that term dubiously) code. Unless otherwise noted in the code, all values being manipulated and/or stored are NSString:
NSArray *evalBooks = [self getEntitiesByName:kOC_EntityName_EvalBook
usingPredicateValue:[NSString stringWithFormat:#"%# > \"\"", kOC_Eval_Bookcode]
withSubstitutionVariables:nil
inModel:[self managedObjectModel]
andContext:[self managedObjectContext]
sortByAttribute:nil];
if ( ( !evalBooks ) || ( [evalBooks count] == 0 ) ) {
// we have problem
NSLog(#"( !evalBooks ) || ( [evalBooks count] == 0 )");
return;
}
[evalBooks retain];
int firstEvalBook = 0;
int thisEvalBook = firstEvalBook;
int lastEvalBook = [evalBooks count]; NSLog(#"lastEvalBook: %i", lastEvalBook);
for (thisEvalBook = firstEvalBook; thisEvalBook < lastEvalBook; thisEvalBook++) {
NSManagedObject *evalBook = [[evalBooks objectAtIndex:thisEvalBook] retain];
NSString *rawIsbn = [[evalBook valueForKey:kOC_Eval_Bookcode] retain];
NSString *isbnRoot = [[self getIsbnRootFromIsbn:rawIsbn] retain];
// this is a custom method I created and use elsewhere without any issues.
NSArray *foundBooks = [self getEntitiesByName:kOC_EntityName_TargetBook
usingPredicateValue:[NSString stringWithFormat:#"%# CONTAINS %#", kOC_Target_PrintBookCode, isbnRoot]
withSubstitutionVariables:nil
inModel:[self managedObjectModel]
andContext:[self managedObjectContext]
sortByAttribute:kOC_Target_PrintBookCode];
if ( foundBooks != nil ) {
[foundBooks retain];
NSLog(#"foundBooks: %lu", [foundBooks count]);
} else {
}
If you're building your predicate as an NSString, I believe
[NSString stringWithFormat:#"%# CONTAINS %#", kOC_Target_PrintBookCode, isbnRoot]
should actually be
[NSString stringWithFormat:#"%# CONTAINS '%#'", kOC_Target_PrintBookCode, isbnRoot]
It seems that you're confusing the way predicateWithFormat: works with the way stringWithFormat: works.
Presumably either kOC_Target_PrintBookCode or isbnRoot is not an object that can be converted to a string. E.g. if either is an integer, the %# operator cannot convert the integer to a string value.