proper NSURL request encoding - iphone

I need to encode the body of my POST request but I don´t know which one to use.
I´m having a trouble with changing ´(´ into %28.
I was using so far NSUTF8StringEncoding, NSISOLatin1StringEncoding, NSISOLatin2StringEncoding. Non of them works.
The name of the parameter I have to send is:
monitoring_report[monitoring_report_time(1i)]=
which sould be transformed to:
monitoring_report%5monitoring_report_time%281i%29%5D=
and what I get is:
monitoring_report%5Bmonitoring_report_time(1i)%5D=

Use the Core Foundation function CFURLCreateStringByAddingPercentEscapes which has a legalURLCharactersToBeEscaped parameter. Parenthesis in URLs are legal which is why they're not escaped by default.
Example:
NSString *input = #"monitoring_report[monitoring_report_time(1i)]=";
NSString *output = [(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)input, NULL, CFSTR("()"), kCFStringEncodingUTF8) autorelease];
NSLog(#"%#", output); // monitoring_report%5Bmonitoring_report_time%281i%29%5D=

Related

NSString behaving weird when set as HTTP header of NSMutableURLRequest

I am putting together a string with certain content, that is set as an HTTP header field of NSMutableURLRequest:
NSString *authenticationHeaderString = [NSString stringWithFormat:#"OAuth oauth_callback=\"%#\", oauth_nonce=\"%#\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"%#\", oauth_consumer_key=\"xxxxxxxxx\", oauth_signature=\"%#\", oauth_version=\"1.0\"", [callbackURLString URLEncodedString], nonce, timestampString, [BYOAuthSignatureString signatureStringWithParameters:signingParams URL:[mURLRequest.URL absoluteString] HTTPMethod:mURLRequest.HTTPMethod]];
NSLog(#"authenticationHeaderString: %#", authenticationHeaderString);
[mURLRequest setValue:[NSString stringWithFormat:#"%#", authenticationHeaderString] forHTTPHeaderField:#"Authorization"];
NSLog(#"header:%#", mURLRequest.allHTTPHeaderFields);
In the header field the \" is not converted into "
authenticationHeaderString: OAuth oauth_callback="http%3A%2F%2Fbytolution.com", oauth_nonce="o7QNfKOMBKtdZQmJrbV9QWqVwUkkRK1ABbjfK4hWTQ", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1361735323", oauth_consumer_key="xxxxxxxxxxx", oauth_signature="koNYscKJx3WwC7M4kHsrMpfzYoo=", oauth_version="1.0"
header:{
Authorization = "OAuth oauth_callback=\"http%3A%2F%2Fbytolution.com\", oauth_nonce=\"o7QNfKOMBKtdZQmJrbV9QWqVwUkkRK1ABbjfK4hWTQ\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1361735323\", oauth_consumer_key=\"xxxxxxxxxxxx\", oauth_signature=\"koNYscKJx3WwC7M4kHsrMpfzYoo=\", oauth_version=\"1.0\"";
}
Why is that?
This is just the way that the NSDictionary mURLRequest.allHTTPHeaderFields is printing itself. Whenever NSDictionaries print their contents they double-quote any strings that include a space. They also escape any double-quotes in the string, among other things.
The string has not been changed. You can verify this by doing
NSLog(#"authHeader: %#", [mURLRequest.allHTTPHeaderFields objectForKey: #"Authorization"]);
It is converted... But then it is converted back for displaying.
Your header should be fine, just it is displayed as a string for logging.

StringByAddingPercentEscapes not working on ampersands, question marks etc

I'm sending a request from my iphone-application, where some of the arguments are text that the user can enter into textboxes. Therefore, I need to HTML-encode them.
Here's the problem I'm running into:
NSLog(#"%#", testText); // Test & ?
testText = [testText stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"%#", testText); // Test%20&%20?
As you can see, only the spaces are encoded, making the server disregard everything past the ampersand for the argument.
Is this the advertised behaviour of stringByAddingPercentEscapes? Do I have to manually replace every special character with corresponding hex code?
Thankful for any contributions.
They are not encoded because they are valid URL characters.
The documentation for stringByAddingPercentEscapesUsingEncoding: says
See CFURLCreateStringByAddingPercentEscapes for more complex transformations.
I encode my query string parameters using the following method (added to a NSString category)
- (NSString *)urlEncodedString {
CFStringRef buffer = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)self,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]"),
kCFStringEncodingUTF8);
NSString *result = [NSString stringWithString:(NSString *)buffer];
CFRelease(buffer);
return result;
}

iPhone SDK - stringWithContentsOfUrl ASCII characters in HTML source

When I fetch the source of any web page, no matter the encoding I use, I always end up with &# - characters (such as © or ®) instead of the actual characters themselves. This goes for foreign characters as well (such as åäö in swedish), which I have to parse from "&Aring" and such).
I'm using
+stringWithContentsOfUrl: encoding: error;
to fetch the source and have tried several different encodings such as NSUTF8StringEncoding and NSASCIIStringEncoding, but nothing seems to affect the end result string.
Any ideas / tips / solution is greatly appreciated! I'd rather not have to implement the entire ASCII table and replace all occurrances of every character... Thanks in advance!
Regards
I'm using
+stringWithContentsOfUrl: encoding: error;
to fetch the source and have tried several different encodings such as NSUTF8StringEncoding and NSASCIIStringEncoding, but nothing seems to affect the end result string.
You're misunderstanding the purpose of that encoding: argument. The method needs to convert bytes into characters somehow; the encoding tells it what sequences of bytes describe which characters. You need to make sure the encoding matches that of the resource data.
The entity references are an SGML/XML thing. SGML and XML are not encodings; they are markup language syntaxes. stringWithContentsOfURL:encoding:error: and its cousins do not attempt to parse sequences of characters (syntax) in any way, which is what they would have to do to convert one sequence of characters (an entity reference) into a different one (the entity, in practice meaning single character, that is referenced).
You can convert the entity references to un-escaped characters using the CFXMLCreateStringByUnescapingEntities function. It takes a CFString, which an NSString is (toll-free bridging), and returns a CFString, which is an NSString.
Are you sure they originally are not in Å form? Try to view the source code in a browser first.
That really, really sucks. I wanted to convert it directly and the above solution isn't really a good one, so I just wrote my own ascii-table converter (static) class. Works as it should have worked natively (though I have to fill in the ascii table myself...)
Ideas for optimization? ("ASCII" is a static NSDictionary)
#implementation InternetHelper
+(NSString *)HTMLSourceFromUrlWithString:(NSString *)str convertASCII:(BOOL)state
{
NSURL *url = [NSURL URLWithString:str];
NSString *source = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
if (state)
source = [InternetHelper ConvertASCIICharactersInString:source];
return source;
}
+(NSString *)ConvertASCIICharactersInString:(NSString *)str
{
NSString *ret = [NSString stringWithString:str];
if (!ASCII)
{
NSString *path = [[NSBundle mainBundle] pathForResource:kASCIICharacterTableFilename ofType:kFileFormat];
ASCII = [[NSDictionary alloc] initWithContentsOfFile:path];
}
for (id key in ASCII)
{
ret = [ret stringByReplacingOccurrencesOfString:key withString:[ASCII objectForKey:key]];
}
return ret;
}
#end

NSXMLParser rss issue NSXMLParserInvalidCharacterError

NSXMLParserInvalidCharacterError # 9
This is the error I get when I hit a weird character (like quotes copied and pasted from word to the web form, that end up in the feed). The feed I am using is not giving an encoding, and their is no hope for me to get them to change that. This is all I get in the header:
< ?xml version="1.0"?>
< rss version="2.0">
What can I do about illegal characters when parsing feeds? Do I sweep the data prior to the parse? Is there something I am missing in the API? Has anyone dealt with this issue?
NSString *dataString = [[[NSString alloc] initWithData:webData encoding:NSASCIIStringEncoding] autorelease];
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
Fixed my problems...
The NSString -initWithData:encoding: method returns nil if it fails, so you can try one encoding after another until you find one that converts. This doesn't guarantee that you'll convert all the characters correctly, but if your feed source isn't sending you correctly encoded XML, then you'll probably have to live with it.
The basic idea is:
// try the most likely encoding
NSString xmlString = [[NSString alloc] initWithData:xmlData
encoding:NSUTF8StringEncoding];
if (xmlString == nil) {
// try the next likely encoding
xmlString = [[NSString alloc] initWithData:xmlData
encoding:NSWindowsCP1252StringEncoding];
}
if (xmlString == nil) {
// etc...
}
To be generic and robust, you could do the following until successful:
1.) Try the encoding specified in the Content-Type header of the HTTP response (if any)
2.) Check the start of the response data for a byte order mark and if found, try the indicated encoding
3.) Look at the first two bytes; if you find a whitespace character or '<' paired with a nul/zero character, try UTF-16 (similarly, you can check the first four bytes to see if you have UTF-32)
4.) Scan the start of the data looking for the <?xml ... ?> processing instruction and look for encoding='something' inside it; try that encoding.
5.) Try some common encodings. Definitely check Windows Latin-1, Mac Roman, and ISO Latin-1 if your data source is in English.
6.) If none of the above work, you could try removing all bytes greater than 127 (or substitute '?' or another ASCII character) and convert the data using the ASCII encoding.
If you don't have an NSString by this point, you should fail. If you do have an NSString, you should look for the encoding declaration in the <?xml ... ?> processing instruction (if you didn't already in step 4). If it's there, you should convert the NSString back to NSData using that encoding; if it's not there, you should convert back using UTF-8 encoding.
Also, the CFStringConvertIANACharSetNameToEncoding() and CFStringConvertEncodingToNSStringEncoding() functions can help get the NSStringEncoding that goes with the encoding name form the Content-Type header or the <?xml ... ?> processing instruction.
You can also remove that encoding line from xml like this:
int length = str.length >100 ? 100:str.length;
NSString*mystr= [str stringByReplacingOccurrencesOfString:#"encoding=\".*?\""
withString:#""
options:NSRegularExpressionSearch
range:NSMakeRange(0, length)];

How do I encode "&" in a URL in an HTML attribute value?

I'd like to make a URL click able in the email app. The problem is that a parameterized URL breaks this because of "&" in the URL. The body variable below is the problem line. Both versions of "body" are incorrect. Once the email app opens, text stops at "...link:". What is needed to encode the ampersand?
NSString *subject = #"This is a test";
NSString *encodedSubject =
[subject stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//NSString *body = #"This is a link: <a href='http://somewhere.com/two.woa/wa?id=000&param=0'>click me</a>"; //original
NSString *body = #"This is a link: <a href='http://somewhere.com/two.woa/wa?id=000&param=0'>click me</a>"; //have also tried &
NSString *encodedBody =
[body stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *formattedURL = [NSString stringWithFormat: #"mailto:myname#somedomain.com?subject=%#&body=%#", encodedSubject, encodedBody];
NSURL *url = [[NSURL alloc] initWithString:formattedURL];
[[UIApplication sharedApplication] openURL:url];
the ampersand would be %26 for HEX in URL Encoding standards
I've been using -[NSString gtm_stringByEscapingForURLArgument], which is provided in Google Toolbox for Mac, specifically in GTMNSString+URLArguments.h and GTMNSString+URLArguments.m.
You can use a hex representation of the character, in this case %26.
you can simply use CFURLCreateStringByAddingPercentEscapes with CFBridgingRelease for ARC support
NSString *subject = #"This is a test";
// Encode all the reserved characters, per RFC 3986
// (<http://www.ietf.org/rfc/rfc3986.txt>)
NSString *encodedSubject =
(NSString *) CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)subject,
NULL,
(CFStringRef)#"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8));
You use stringByAddingPercentEscapesUsingEncoding, exactly like you are doing.
The problem is that you aren't using it enough. The format into which you're inserting the encoded body also has an ampersand, which you have not encoded. Tack the unencoded string onto it instead, and encode them (using stringByAddingPercentEscapesUsingEncoding) together.
<a href='http://somewhere.com/two.woa/wa?id=000&param=0'>click me</a>
Is correct, although ‘&’ is more commonly used than ‘&’ or ‘,’.
If the ‘stringByAddingPercentEscapesUsingEncoding’ method does what it says on the tin, it should work(*), but the NSString documentation looks a bit unclear on which characters exactly are escaped. Check what you are ending up with, the URL should be something like:
mailto:bob#example.com?subject=test&body=Link%3A%3Ca%20href%3D%22http%3A//example.com/script%3Fp1%3Da%26amp%3Bp2%3Db%22%3Elink%3C/a%3E
(*: modulo the usual disclaimer that mailto: link parameters like ‘subject’ and ‘body’ are non-standard, will fail in many situations, and should generally be avoided.)
Once the email app opens, text stops at "...link:".
If ‘stringByAddingPercentEscapesUsingEncoding’ is not escaping ‘<’ to ‘%3C’, that could be the problem. Otherwise, it might not be anything to do with escapes, but a deliberate mailer-level restriction to disallow ‘<’. As previously mentioned, ?body=... is not a reliable feature.
In any case, you shouldn't expect the mailer to recognise the HTML and try to send an HTML mail; very few will do that.
Example of use of %26 instead of & without this attributes arrived in PHP as an array!
var urlb='/tools/lister.php?type=101%26ID='+ID; // %26 instead of &
window.location.href=urlb;