NSString writeToFile with URL encoded string - objective-c

I have a Mac application that keeps it's own log file. It appends info to the file using NSString's writeToFile method. One of the things that it logs are URL's of web services that it is interacting with. To encode the URL, I'm doing this:
searchString = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)searchString, NULL, (CFStringRef)#"!*'();:#&=+$,/?%#[]", kCFStringEncodingUTF8 );
The app then appends searchString to the rest of the URL and writes it to the log file. Now the problem is that after adding that URL encoding line, nothing seems to be getting written to the file. The program functions as expected otherwise however. Removing the line of code above results in all of the correct information being logged to the file (removing that line is not an option because searchString must be URL encoded).
Oh and I am using NSUTF8StringEncoding when writing the NSString to the file.
Thanks for any help.
EDIT: I know there's also a similar function to CFURLCreateStringByAddingPercentEscapes in NSString, but I've read that it doesn't always work. Can anyone shed some light on this if my original question cannot be answered? Thanks! (EDIT: same problem occurs when using stringByAddingPercentEscapesUsingEncoding:)
EDIT 2: Here's the code that I'm using to append messages to the log file.
+(void)logText:(NSString *)theString{
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,NSUserDomainMask,YES) objectAtIndex:0];
NSString *path = [docsDirectory stringByAppendingPathComponent:#"Folder/File.log"];
NSString *fileContents = [[[NSString alloc] initWithContentsOfFile:path] autorelease];
if([fileContents lengthOfBytesUsingEncoding:NSUTF8StringEncoding] >= 204800){
fileContents = #"";
}
NSString *timeStamp = [[NSDate date] description];
timeStamp = [timeStamp stringByAppendingString:#": "];
timeStamp = [timeStamp stringByAppendingString:theString];
fileContents = [fileContents stringByAppendingString:timeStamp];
fileContents = [fileContents stringByAppendingString:#"\n"];
[fileContents writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
}

Because after almost a whole day no one else has offered any answers, I'm going to post a wild guess here: you're not accidentally using the string you want to output (with percent characters in it) as a format string are you?
That is, making the mistake of doing:
NSLog(#"In format strings you can use %# as a placeholder for an object, and %i for a plain C integer.")
Instead of:
NSLog(#"%#", #"In format strings you can use %# as a placeholder for an object, and %i for a plain C integer.");
But I'm going to be surprised if this turns out to be the cause of your problem, as it usually causes random-looking output, rather than absolutely no output. And in some cases, Xcode also gives compiler warnings about it (when I tried NSLog(myString), I got "warning: format not a string literal and no format arguments").
So don't shoot me down if this answer doesn't help. It would be easier to answer your question if you could show us more of your logging code. As for the one line you provided, I can't detect anything wrong with it.
Edit: Oops, I kind of missed that you mentioned you're using writeToFile:atomically:encoding:error: to write the string to the file, so it's even more unlikely you're accidentally treating it as a format string somewhere. But I'm going to leave this answer up for now. Again, you should really show us more of your code though ...
Edit: Regarding your question on a method in NSString that has similar percent encoding functionality, that would be stringByAddingPercentEscapesUsingEncoding:. I'm not sure what kind of problems you're thinking of when you say you've heard it doesn't always work. But one thing is that CFURLCreateStringByAddingPercentEscapes allows you to specify extra characters that don't normally have to be escaped but which you still want to be escaped, while the method of NSString doesn't allow you to specify this.

Related

Can't get MD5 checksum from security framework on Mac OS X Mountain Lion

NSString *curFourChanFilePath = [currentSubFile stringByAppendingPathComponent:curFourChanFile];
NSData *imageData = [NSData dataWithContentsOfFile:curFourChanFilePath];
CFErrorRef theError;
SecTransformRef testTransform = SecDigestTransformCreate(kSecDigestMD5,0,&theError);
CFDataRef theDataRef = (__bridge CFDataRef)imageData;
SecTransformSetAttribute(testTransform, kSecTransformInputAttributeName, theDataRef, &theError);
NSData *resultingData = (__bridge NSData *)(SecTransformExecute(testTransform, &theError));
NSString *resultingString = [[NSString alloc] initWithData:resultingData encoding:NSUTF8StringEncoding];
NSLog(#"%#",resultingString);
[checksumMapTable setObject:resultingData forKey:curFourChanFile];
Here's the code I'm having issues with. This code is in a nested nested loop, and all the code works fine till it get to turning the data into an NSString. It seems to have trouble with UTF8. All the strings turn into (null), but the strange thing is, it isn't (null). When I change the encoding to UTF16 or UTF32, I get text. Not readable text, it's all garbled as you'd expect from using the wrong encoding, but it's clearly there, I just can't seem to get at it in what I thought was the proper encoding, UTF8. Any help would be appreciated. Just to reiterate, again, all the code seems to be working fine until this point. The Security framework is still a bit new to me.
Actually, I just answered my own question. using SecEncodeTransformCreate(NULL, NULL) and sending the data through that, I got the checksum. Problem solved.
Change your encoding to one that can decode any byte value. For example NSISOLatin1StringEncoding. This should give you an output similar to openssl md5 -binary.
Not that the result will make much sense though…

Using libqrencode library

I loaded libqrencode library in my cocoa project but I'm not sure how to use it exactly. I have a text field in which you type a text, and once done you click a button and I log that text with NSLog. Now I want to encode that text to be able to use it later and generate a QRcode out of it, so in the manual it's saying to use this format
QRcode* QRcode_encodeString (const char * string,
int version,
QRecLevel level,
QRencodeMode hint,
int casesensitive
)
I am not sure how to use that in my method to log the results as well
- (IBAction)GenerateCode:(id)sender {
NSString *urlText = [[NSString alloc] initWithFormat:#"%#", [_urlField stringValue]];
NSLog(#"The url is %#", urlText);
}
You need to get from an NSString instance to a const char *. This has been answered several times on SO, but here's one. Once you do that, you can call QRCode_encodeString() directly and pass whatever you desire for the arguments.
If you need more specifics, you'll have to try something, post your code, and describe how it's not working for you so we can help you more directly without just writing it for you.

Removing unicode and backslash escapes from NSString converted from NSData

I am converting the response data from a web request to an NSString in the following manner:
NSData *data = self.responseData;
if (!data) {
return nil;
}
NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)[self.response textEncodingName]));
NSString *responseString = [[NSString alloc] initWithData:data encoding:encoding];
However the resulting string looks like this:
"birthday":"04\/01\/1990",
"email":"some.address\u0040some.domain.com"
What I would like is
"birthday":"04/01/1990",
"email":"some.address#some.domain.com"
without the backslash escapes and unicode. What is the cleanest way to do this?
The response seems to be JSON-encoded. So simply decode the response string using a JSON library (SBJson, JsonKit etc.) to get the correct form.
You can replace (or remove) characters using NSString's stringByReplacingCharactersInRange:withString: or stringByReplacingOccurrencesOfString:withString:.
To remove (convert) unicode characters, use dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES (from this answer).
I'm sorry if the following has nothing to do with your case: Personally, I would ask myself where did that back-slashes come from in the first place. For example, for JSON, I'd know that some sort of JSON serializer on the other side escapes some characters (so the slashes are really there, in the response, and that is not a some weird bug in Cocoa). That way I'd able to tell for sure which characters I have to handle and how. Or maybe I'd use some kind of library to do that for me.

Reading String from File with Objective C

This one is weird. Hopefully I will ask the right question:
I'm using an md5 method to create a checksum value which I then write to a file. Then afterwards I read the file using this:
NSString * id_From_File = [[NSString alloc]
initWithContentsOfFile:path_to_ID
encoding:NSUTF8StringEncoding
error:&error];
The result gets placed in a NSString which when I print gives me very strange behaviour. For example when I use this to print,
id_with_date = [NSString stringWithFormat:#" %# %#", dateString, id_From_File];
it will print both strings if dateString is placed in the first parameter and id_From_File in the second. If I switch them around (which I need to do) only id_From_File shows.
Edit 1: Example of the switch:
id_with_date = [NSString stringWithFormat:#" %# %#", id_From_File, dateString];
I strongly believe this has something to do with the encoding of the id_From_File string.
Any knowledge!?
Thanks,
NSString should actually be capable of recognizing null characters as the file ending. Did you try to use a different method to load the string. I'd go for this one instead:
- (id)initWithContentsOfFile:(NSString *)path usedEncoding:(NSStringEncoding *)enc error:(NSError **)error
This method automatically detects the file's encoding instead of decoding it with a fixed one.
I've solved the problem!
It has to do with the fact that some strings use a null character to identify the end. Allow me to explain:
Lets say you have two strings, one with a null character at the end and one that doesn't. Depending on which way you order them, they will be read differently when concatenated.
"somestring(null char)" + "another string"
The above, in some code, will read
somestring
if places are switched
"another string" + "somestring(null char)"
then you get
"another string somestring"
My simple hack to fix this was to make a new string with a substring of "some string" which easily got rid of that last char that was causing the bug.
I hope this is clear and helpful!

Warning: "format not a string literal and no format arguments"

Since upgrading to the latest Xcode 3.2.1 and Snow Leopard, I've been getting the warning
"format not a string literal and no format arguments"
from the following code:
NSError *error = nil;
if (![self.managedObjectContext save:&error])
{
NSLog([NSString stringWithFormat:#"%# %#, %#",
errorMsgFormat,
error,
[error userInfo]]);
}
If errorMsgFormat is an NSString with format specifiers (eg: "print me like this: %#"), what is wrong with the above NSLog call? And what is the recommended way to fix it so that the warning isn't generated?
Xcode is complaining because this is a security problem.
Here's code similar to yours:
NSString *nameFormat = #"%# %#";
NSString *firstName = #"Jon";
NSString *lastName = #"Hess %#";
NSString *name = [NSString stringWithFormat:nameFormat, firstName, lastName];
NSLog(name);
That last NSLog statement is going to be executing the equivalent of this:
NSLog(#"Jon Hess %#");
That's going to cause NSLog to look for one more string argument, but there isn't one. Because of the way the C language works, it's going to pick up some random garbage pointer from the stack and try to treat it like an NSString. This will most likely crash your program. Now your strings probably don't have %#'s in them, but some day they might. You should always use a format string with data you explicitly control as the first argument to functions that take format strings (printf, scanf, NSLog, -[NSString stringWithFormat:], ...).
As Otto points out, you should probably just do something like:
NSLog(errorMsgFormat, error, [error userInfo]);
Are you nesting your brackets correctly? I don't think NSLog() likes taking only one argument, which is what you're passing it. Also, it already does the formatting for you. Why not just do this?
NSLog(#"%# %#, %#",
errorMsgFormat,
error,
[error userInfo]);
Or, since you say errorMsgFormat is a format string with a single placeholder, are you trying to do this?
NSLog(#"%#, %#", [NSString stringWithFormat:errorMsgFormat, error],
[error userInfo]);
Final answer: As Jon Hess said, it's a security issue because you're passing a WHATEVER string to a function expecting a format string. That is, it'll evaluate all format specifiers WITHIN the whatever string. If there aren't any, awesome, but if there are, bad things could happen.
The proper thing to do, then, is USE a format string directly, for example
NSLog(#"%#", myNSString);
That way, even if there are format specifiers in myNSString, they don't get evaluated by NSLog.
I don't especially recommend using this, since the warning IS a real warning.. in a dynamic use of the language it's possible to do things runtime to the string (i.e. insert new information or even crash the program).. However it's possible to force suppress if you KNOW that it should be like this and you really don't want to be warned about it..
#pragma GCC diagnostic ignored "-Wformat-security"
Would tell GCC to temporarily ignore the compilation warning.. Again it's not solving anything but there may be times when you can't find a good way to actually fix the problem.
EDIT: As of clang, the pragma has changed. See this: https://stackoverflow.com/a/17322337/3937
Quickest way to fix it would be to add #"%#", as the first argument to your NSLog call, i.e.,
NSLog(#"%#", [NSString stringWithFormat: ....]);
Though, you should probably consider Sixteen Otto's answer.
I've just been passing a nil to negate the warnings, maybe that would work for you?
NSLog(myString, nil);
If you want get rid of the warning "format not a string literal and no format arguments" once and for all, you can disable the GCC warning setting "Typecheck Calls to printf/scanf" (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO) in your target's build settings.
NSLog() expects a format string, what is getting passed in is just a string. You do not need to use stringWithFormat:, you can just do:
NSLog(#"%# %#, %#", errorMsgFormat, error, [error userInfo])
And that would make the warning go away.
FWIW, this applies to iPhone dev as well. I'm coding against the 3.1.3 SDK, and got the same error with the same problem (nesting stringWithFormat inside NSLog()). Sixten and Jon are on the money.
Just letting anyone know using the appendFormat on NSMutableString can also cause this warning to appear if trying to pass in a formatted string like so:
NSMutableString *csv = [NSMutableString stringWithString:#""];
NSString *csvAddition = [NSString stringWithFormat:#"%#",WHATEVERYOUAREPUTTINGINYOURSTRING];
[csv appendFormat:csvAddition];
So to avoid this warning, turn the above into this:
NSMutableString *csv = [NSMutableString stringWithString:#""];
[csv appendFormat:#"%#",WHATEVERYOUAREPUTTINGINYOURSTRING];
More concise and more secure. Enjoy!
NSLog(#"%# %#, %#",
errorMsgFormat,
error,
[error userInfo]);