portuguese characters with webservice objective c - objective-c

i am calling a webservice and receiving data with portuguese characters
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request addValue:#"application/json; charset=ISO-8859-1" forHTTPHeaderField:#"Content-Type"];
NSDictionary *allDataDictionary = [NSJSONSerialization JSONObjectWithData:webData options:NSISOLatin1StringEncoding0 error:nil];
even added the iso-8859-1 to the header but always receive a dictionary like this:
{
name = "Lu\U00eds Brito";
user = lbrito;
userId = 16;
},
{
name = "Nelson Azevedo";
user = nazevedo;
userId = 17;
},
{
name = "M\U00f3nica Morais";
user = mmorais;
userId = 19;
},
tried to put in NSISOLatin1StringEncoding the options but doesnt do anything
anyone knows a solution, searched around didnt find much help, thks.

JSON is defined to be always encoded in UTF-8 (it can use UTF-16 and UTF-32 as well, but not ISO 8859-1)

Related

AWS S3 POST File from IOS client Using AFNetworking

I am hoping, through this post, not just to get an answer but to help many others struggling with using AWS secure POST policy infrastructure. Code is shown, after the explanation.
We use an EC2 Ubuntu 20.2 instance running Django as server. Using boto3 (s3_client.generate_presigned_post) we create a RetrieveAPIView using rest_framework. This Generic View accepts 3 GET parameters - file_name, meta_uuid & mime_type - and generates the POST signature and policy. This view returns the "fields" dictionary, url, etc. in the form of JSON. Boto3 ignores the value entered for "x-amz-date" and generates its own.
Using Postman we can successfully retrieve a full policy from the Ubuntu server. Then, after copying/pasting the values for the returned fields we use Postman to post the file to AWS using form-data in the body. The file uploads fine and we get the HTTP 204 No Content and the other fields in the Header. If we make a small change to one of the fields during the post to AWS S3, the server comes back with HTTP 403 Forbidden & the code "SignatureDoesNotMatch", just what one would expect.
BUT, when attempting to run from EITHER the Xcode simulator or from an actual device using AFNetworking's [AFHTTPSessionManager uploadTaskWithRequest: fromFile: progress: completionHandler:] we get back from AWS S3 - HTTP 400 Bad Request and the code "IncompleteBody" in the xml. AWS documentation suggests that the problem is that the content length measured at S3 not consistent with the Content-Length being sent in the header.
The file size, when measured using iOS FileManger is 111251 . AFNetworking outbound Header is Content-Length = 113071. That makes sense when one counts the length of the encoded policy, signature, other fields, the boundary value, etc.
Any hints on what to look for would be greatly appreciated. Does AWS only look at the file size when calculating Content-Length? Does this mean that the policy is OK, or does AWS S3 first just do some rudimentary header checking before looking at the encoded policy and signature?
IOS using AFNetworking 3.0 (CocoaPod), Xcode 13.1.
Thanks,
Eric
NB: Policy encodings were fudged so as not to expose our MY_AWS_ASSIGNED_S3_KEY_ID
Code follows and hoping it helps many other dealing with this same issue:
Server side Django:
def retrieve(self, request, *args, **kwargs):
s3_client = boto3.client('s3', aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),
region_name=AWS_REGION_NAME,
config=config.Config(signature_version='s3v4'))
t = datetime.datetime.utcnow()
amz_date = t.strftime('%Y%m%d') # Date w/o time, used in credential scope
amz_date_z = amz_date + 'T000000Z'
amz_cred = os.getenv('AWS_ACCESS_KEY_ID') + '/' + amz_date + '/' + AWS_REGION_NAME + '/' + 's3/aws4_request'
cont_type = request.GET.get('mime_type', None)
meta_uuid = request.GET.get('meta_uuid', None)
fields_dict = {
'acl': 'authenticated-read',
'bucket': AWS_STORAGE_BUCKET_NAME,
'x-amz-algorithm': 'AWS4-HMAC-SHA256',
'x-amz-meta-uuid': meta_uuid,
'x-amz-credential': amz_cred,
'x-amz-date': amz_date_z,
'content-type': cont_type
}
cond_list = [
{'content-type': cont_type},
{'bucket': AWS_STORAGE_BUCKET_NAME},
{'x-amz-meta-uuid': meta_uuid},
{'acl': 'authenticated-read'},
]
ret_dict = s3_client.generate_presigned_post(
Bucket=AWS_STORAGE_BUCKET_NAME,
Key=PRIVATE_MEDIA_LOCATION + request.GET.get('file_name', None),
Fields=fields_dict,
Conditions=cond_list,
ExpiresIn=3600
)
print("ret_dict =")
print(ret_dict)
return Response({'status': 'success', 'data': ret_dict}, status=status.HTTP_202_ACCEPTED)
Returned JSON from server to Postman:
{
"status": "success",
"data": {
"url": "https://aa-dev-media.s3.amazonaws.com/",
"fields": {
"acl": "authenticated-read",
"bucket": "aa-dev-media",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-meta-uuid": "some_random_string",
"x-amz-credential": "**MY_AWS_ASSIGNED_S3_KEY_ID**/20211107/us-west-2/s3/aws4_request",
"x-amz-date": "20211107T092556Z",
"content-type": "image/jpeg",
"key": "media/krTkQskg.jpg",
"policy": "eyJleHBpcmF0aW9uIjogIjIwMjEtMTEtMDdUMTA6MjU6NTZaIiwgImNvbmRpdGlvbnMiOiBbeyJjb250ZW50LXR5cGUiOiAiaW1hZ2UvanBlZyJ9LCB7ImJ1Y2tldCI6ICJhYS1kZXYtbWVkaWEifSwgeyJ4LWFtei1tZXRhLXV1aWQiOiAic29tZV9yYW5kb21fc3RyaW5nIn0sIHsiYWNsIjogImF1dGhlbnRpY2F0ZWQtcmVhZCJ9LCB7ImJ1Y2tldCI6ICJhYS1kZXYtbWVkaWTTTSwgeyJrZXkiOiAibWVkaWEva3JUa1Fza2cuanBnIn0sIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwgeyJ4LWFtei1jcmVkZW50aWFsIjogIkFLSUE2UVZNTFlWNVRSUk9YQjVZLzIwMjExMTA3L3VzLXdlc3QtMi9zMy9hd3M0X3JlcXVlc3QifSwgeyJ4LWFtei1kYXRlIjogIjIwMjExMTA3VDA5MjU1NloifV19",
"x-amz-signature": "8fcec036a12ae2ab9212133c5c7ce275e91961ab9486936a47ce0e6bb869e6ce"
}
}
}
Objective C code in App:
- (void)uploadCurrentMediaFileToS3ForAPI:(APIType)api usingParameters:(NSDictionary *)pDict
{
NSLog(#"%s fileURL = %#",__FUNCTION__,currentMFO.file_url);
NSLog(#"%s fName = %#",__FUNCTION__,currentMFO.file_name);
NSLog(#"%s mType = %#",__FUNCTION__,currentMFO.mime_mfo.mime_type);
NSLog(#"%s httpPath = %#",__FUNCTION__,httpPath);
NSLog(#"%s pDict = %#",__FUNCTION__,pDict);
NSLog(#"%s currentMFO.file_size_bytes = %lld",__FUNCTION__,currentMFO.file_size_bytes);
NSError *reqError = nil;
AFHTTPRequestSerializer *reqSerial = [AFHTTPRequestSerializer serializer];
NSMutableURLRequest *req = [reqSerial multipartFormRequestWithMethod:kHTTPPOST URLString:httpPath parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData)
{
NSArray *paramKeys = [pDict allKeys];
for (NSString *key in paramKeys)
{
NSString *paramValue = [pDict objectForKey:key];
NSData *valData = [paramValue dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"%s key = %#, value = %# ",__FUNCTION__,key, paramValue);
[formData appendPartWithFormData:valData name:key];
}
BOOL fileFormSuccess = [formData appendPartWithFileURL:self->currentMFO.file_url name:#"file" fileName:self->currentMFO.file_name mimeType:self->currentMFO.mime_mfo.mime_type error:nil];
NSLog(#"%s fileFormSuccess = %i",__FUNCTION__,fileFormSuccess);
} error:&reqError];
if (reqError) [self incrementErrorCountForAPI:api statusCode:100 locDesc:reqError.localizedDescription andReason:reqError.localizedFailureReason];
[req addValue:#"*/*" forHTTPHeaderField:#"Accept"];
[req addValue:#"500" forHTTPHeaderField:#"Keep-Alive"];
NSLog(#"%s after req.allHTTPHeaderFields = %#",__FUNCTION__,req.allHTTPHeaderFields);
NSLog(#"%s req.URL = %#",__FUNCTION__,req.URL);
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:req fromFile:currentMFO.file_url progress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error)
{
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse *)response;
NSLog(#"%s httpResp.allHeaderFields - %#",__FUNCTION__,httpResp.allHeaderFields);
NSLog(#"%s httpResp.MIMEType - %#",__FUNCTION__,httpResp.MIMEType);
NSLog(#"%s httpResp.statusCode - %ld",__FUNCTION__,(long)httpResp.statusCode);
if (responseObject)
{
NSString *respString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"%s respString - %#",__FUNCTION__,respString);
}
}];
[uploadTask resume];
}
And here are the logs from the Xcode debugger:
fileURL = file:///var/mobile/Containers/Data/Application/8FF62939-0E50-4582-B242-01472D1C75D3/Library/UserMedia/horiz_windlass_photo.png
fName = horiz_windlass_photo.png
mType = image/png
httpPath = https://aa-dev-media.s3.amazonaws.com/
pDict = {
acl = "authenticated-read";
bucket = "aa-dev-media";
"content-type" = "image/png";
key = "media/horiz_windlass_photo.png";
policy = "eyJleHBpcmF0aW9uIjogIjIwMjEtMTEtMDdUMTE6MjM6MDNaIiwgImNvbmRpdGlvbnMiOiBbeyJjb250ZW50LXR5cGUiOiAiaW1hZ2UvcG5nIn0sIHsiYnVja2V0IjogImFhLWRldi1tZWRpYSJ9LCB7IngtYW16LW1ldGEtdXVpZCI6ICJob3Jpel93aW5kbGFzc19waG90by5wbmcifSwgeyJhY2wiOiAiYXV0aGVudGljYXRlZC1yZWFkIn0sIHsiYnVja2V0IjogImFhLWRldi1tZWRpYSJ9LCB7ImtleSI6ICJtZWRpYS9ob3Jpel93aW5kbGFzc19waG90by5wbmcifSwgeyJ4LXXXXi1hbGdvcml0aG0iOiAiQVdTNC1ITUFDLVNIQTI1NiJ9LCB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQTZRVk1MWVY1VFJST1hCNVkvMjAyMTExMDcvdXMtd2VzdC0yL3MzL2F3czRfcmVxdWVzdCJ9LCB7IngtYW16LWRhdGUiOiAiMjAyMTExMDdUMTAyMzAzWiJ9XX0=";
"x-amz-algorithm" = "AWS4-HMAC-SHA256";
"x-amz-credential" = "**MY_AWS_ASSIGNED_S3_KEY_ID**/20211107/us-west-2/s3/aws4_request";
"x-amz-date" = 20211107T102303Z;
"x-amz-meta-uuid" = "horiz_windlass_photo.png";
"x-amz-signature" = 41677f9254e324553a429bbcc3dcf5bd0f5df4af1c25fcaf51ce088a2a5dd032;
}
currentMFO.file_size_bytes = 111251
key = bucket, value = aa-dev-media
key = content-type, value = image/png
key = policy, value = eyJleHBpcmF0aW9uIjogIjIwMjEtMTEtMDdUMTE6MjM6MDNaIiwgImNvbmRpdGlvbnMiOiBbeyJjb250ZW50LXR5cGUiOiAiaW1hZ2UvcG5nIn0sIHsiYnVja2V0IjogImFhLWRldi1tZWRpYSJ9LCB7IngtYW16LW1ldGEtdXVpZCI6ICJob3Jpel93aW5kbGFzc19waG90by5wbmcifSwgeyJhY2wiOiAiYXV0aGVudGljYXRlZC1yZWFkIn0sIHsiYnVja2V0IjogImFhLWRldi1tZWRpYSJ9LCB7ImtleSI6ICJtZWRpYS9ob3Jpel93aW5kbGFzc19waG90by5wbmcifSwgeyJ4LXXXXi1hbGdvcml0aG0iOiAiQVdTNC1ITUFDLVNIQTI1NiJ9LCB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQTZRVk1MWVY1VFJST1hCNVkvMjAyMTExMDcvdXMtd2VzdC0yL3MzL2F3czRfcmVxdWVzdCJ9LCB7IngtYW16LWRhdGUiOiAiMjAyMTExMDdUMTAyMzAzWiJ9XX0=
key = x-amz-signature, value = 41677f9254e324553a429bbcc3dcf5bd0f5df4af1c25fcaf51ce088a2a5dd032
key = x-amz-algorithm, value = AWS4-HMAC-SHA256
key = acl, value = authenticated-read
key = x-amz-meta-uuid, value = horiz_windlass_photo.png
key = key, value = media/horiz_windlass_photo.png
key = x-amz-credential, value = **MY_AWS_ASSIGNED_S3_KEY_ID**/20211107/us-west-2/s3/aws4_request
key = x-amz-date, value = 20211107T102303Z
after formData = <AFStreamingMultipartFormData: 0x282c6e0d0>
fileFormSuccess = 1
req.allHTTPHeaderFields = {
User-Agent = "AnchorAway/1.0 (iPhone; iOS 15.0.2; Scale/3.00)”,
Accept-Language = "en-US;q=1”,
Content-Type = "multipart/form-data; boundary=Boundary+5D68022C08D21CC8”,
Content-Length = 113071,
Accept = “*/*”,
Keep-Alive = "500"
]
req.HTTPBody = (null)
eq.URL = https://aa-dev-media.s3.amazonaws.com/}
httpResp.allHeaderFields - {
Server = AmazonS3
Content-Type = "application/xml"
Transfer-Encoding = "Identity"
x-amz-request-id = "2BXQ22BB603CA92E"
Date = "Sun, 07 Nov 2021 10:23:03 GMT"
x-amz-id-2 = "tmvZxT96gJvL4SwxTdlHhkh3ZrGj1vmiG4JO1MQPyJc3bYlXMpofLxfImLuhBZEddSAS7nUdAzc="
Connection = close
}
httpResp.MIMEType - application/xml
httpResp.statusCode - 400
respString - <?xml version="1.0" encoding="UTF-8"?><Error><Code>IncompleteBody</Code><Message>The request body terminated unexpectedly</Message> <RequestId>2BXQ22BB603CA92E<RequestId><HostId>tmvZxT96gJvL4SwxTdlHhkh3ZrGj1vmiG4JO1MQPyJc3bYlXMpofLxfImLuhBZEddSAS7nUdAzc=</HostId></Error>
I found the solutions and willing to share it. If you like or use it, please vote 'yes' so perhaps I get enough visibility so that next time someone posts advise... Approach should also work just fine in Swift...
Using AFNetworking's uploadTaskWithRequest:fromFile: in conjunction with its multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error: ------ was a bad idea.
AFNetworking's is a subclass of Apple's uploadTaskWithRequest:fromFile: . Apple Documentation States
"The body stream and body data in this request object are ignored."
Hence, anything formed during construction of the Body was ignored subsequently by uploadTask. AWS never saw a multi-form body or file.
AWS's S3 Response Error Codes cannot be taken literally
That is not to say that they aren't useful, because they are. But, often they are "red herrings" for the real issue, which requires a lot of brute force digging. What saved me on this was using WireShark to examine the packets in the request and response --- extremely helpful - https://www.wireshark.org I highly recommend using this tool.
Manually construct your own form-data body as string, convert to NSData, add data from image file, append with form-data suffix and save to temporary file
You might find this code useful:
NSString * boundaryString = [NSString stringWithFormat:#"------XY%#",SOME_RANDOM_STRING];
NSMutableString *bodyString = [NSMutableString stringWithFormat:#"--%#\r\n",boundaryString];
for (NSString *key in paramKeys)
{
[bodyString appendFormat:#"Content-Disposition: form-data; name=\"%#\"\r\n\r\n",key];
[bodyString appendFormat:#"%#",[paramDict objectForKey:key]];
[bodyString appendFormat:#"\r\n--%#\r\n", boundaryString];
}
[bodyString appendFormat:#"Content-Disposition: form-data; name=\"%#\"; filename=\"%#\"\r\n",#"file",currentMFO.file_name];
[bodyString appendFormat:#"Content-Type: %#\r\n\r\n",currentMFO.mime_mfo.mime_type];
NSMutableData *postData = [NSMutableData dataWithData:[bodyString dataUsingEncoding:NSUTF8StringEncoding]];
NSData *fileData = [NSData dataWithContentsOfFile:IMAGE_FILE_URL];
[postData appendData:fileData];
NSString *suffixString = [NSString stringWithFormat:#"\r\n--%#--\r\n",boundartString];
NSData *suffixData = [NSData dataWithData:[suffixString dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:suffixData];
[[NSFileManager defaultManager] createFileAtPath:PATH_FOR_TEMP_FILE contents:postData attributes:nil];
Finally, passing the temporary file url to AFNetworking's uploadTaskWithRequest:fromFile: should work just fine.

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!

Objective-C URL string contains single percent sign

I have a string which ends with percent sign(%),
this string is prepared for an URL request as a parameter:
NSString *parameter = #"/param=%";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL urlWithString:[NSString stringWithFormat:#"http://www.whatev%#",parameter]]];
The request returns nil.
I've tried:
NSString *parameter = #"/param=\uFF05";
//request returns nil
and
NSString *parameter = #"/param=%";
NSString *newParameter = [parameter stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//request returns /param=%25 ...where does 25 come from?!
How could I have only one % converted to a request url?
Any advice would be appreciated.
The percent sign has a special purpose in URL's and is used to encode special characters of all kinds. For example a space ( ) is %20 and the percent sign itself is %25.
http://en.wikipedia.org/wiki/Percent-encoding
The last one should be correct, I assume you have problems using it as a request?
escape sequence for % is %%, so #"/param=%%" should solve the problem

Objective C JSON object null but responseData not empty

I am sending an HTTP request to a web service and I should get a JSON as response. In Objective C the responseData is not empty, but the serialization of it as JSON is null. This is my code:
- (IBAction)getProfileInfo:(id)sender
{
NSString *code = #"oanure!1";
//Start request
NSURL *url = [NSURL URLWithString:#"http://192.168.1.8:5000/login?json"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:code forKey:#"logincode"];
[request setDelegate:self];
[request startAsynchronous];
}
- (void)requestFinished: (ASIHTTPRequest *) request{
if (request.responseStatusCode == 400){
self.profileInfo.text = #"Invalid code";
} else if (request.responseStatusCode == 403) {
self.profileInfo.text = #"Code alredy used";
} else if (request.responseStatusCode == 200) {
NSData *responseData = [request responseData];
NSLog(#"%#", responseData);
NSError *error;
//I tried with NSDictionary as well
NSArray *json = (NSArray *)
[NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSLog(#"%#", json);
}
else {
self.profileInfo.text = #"Unexpected error";
}
}
The console prints out:
2013-07-02 16:30:16.047 AppName[1396:c07] <3c68746d 6c3e0a0a 3c686561 643e0a3c
7469746c 653e5072 6f66696c 65206f66 206f616e 7572653c 2f746974 6c653e0a 0a3c7363
72697074 20747970 653d2274 6578742f 6a617661 53637269 70742220 7372633d 22737461
7469632f 6a717565 7279312e 392e6a73 223e3c2f 73637269 70743e0a 0a0a3c2f 68656164
3e0a0a3c 626f6479 3e0a3c68 313e4865 6c6c6f2c 206f616e 75726521 3c2f6831 3e0a3c74
61626c65 3e0a2020 3c74723e 3c746820 636f6c73 70616e3d 323e4661 6365626f 6f6b3c2f
74683e3c 2f74723e 0a20203c 74723e0a 20202020 3c74643e 75736572 6e616d65 3c2f7464
3e3c7464 3e6f616e 7572653c 2f74643e 0a20203c 2f74723e 0a3c2f74 61626c65 3e0a3c2f
626f6479 3e0a0a3c 2f68746d 6c3e0a>
2013-07-02 16:30:16.048 AppName[1396:c07] (null)
I suppose I am not doing the serialization right...
Just so to prove that the web service works, I wrote a small script in Python:
import httplib, urllib
params = urllib.urlencode({
'logincode' : 'oanure!1',
})
conn = httplib.HTTPConnection("192.168.1.8:5000")
conn.request("POST", "/login?json",
params)
response = conn.getresponse()
print response.status, response.reason
data = response.read()
print data
conn.close()
In this case the console printed out:
200 OK
{"loggedin": 0, "error": "Not logged in"}
I would like to get the same thing in Objective C.
Please help!
I have tried to convert the responseData using hex to string online converter and it is in HTML (not JSON format). Please check in server side whether is it sending data correctly or not. Here is the converted responseData:
<html>
<head>
<?title>Profile of oanure</title>
<sc?ript type="text/javaScript" src="sta?tic/jquery1.9.js"></script>
</head?>
<body>
<h1>Hello, oanure!</h1>
<t?able>
<tr><th colspan=2>Facebook</?th></tr>
<tr>
<td>username</td?><td>oanure</td>
</tr>
</table>
</?body>
</html>
Hex converter:
http://www.string-functions.com/hex-string.aspx
Secondly, probably you should print out the errMessage as well to check the detailed error. Once JSON couldn't convert the format, it will always return nil (null) to json object.
Similar problems may appear because you are using port an URL. In the case of Python you open the connection at the specified port. Maybe you need to find a solution to work correctly with the port in the ASIFormDataRequest or try to use another library.

Base64 issue in NSMutableURLRequest POST message?

I'm having communcation issues between my app and the server. I'm using RNCryptor to encrypt a message, which I then base64 encode and transfer to the server in the request. This is done in both the DATA header, and within the http body as post data. I think I'm making a mistake in how I'm converting & transferring the base64 encoded message via POST.
If I receive the encrypted message via the header, it decrypts perfectly fine, every single time. However, if I take the message via the POST data, I'm getting varying results. Most of the time, it fails, else it partially decrypts (first few letters), with 1 in 20 or so successful decryptions.
The objective-c code is:
- (NSString *)sendEncryptedTestMessage:(NSString *)address{
NSString* messageContent = #"Hello my name is Bob.";
NSError * error = nil;
NSString* responseString2 = nil;
NSData* postData = [RNEncryptor encryptData:[messageContent dataUsingEncoding:NSUTF8StringEncoding]
withSettings:kRNCryptorAES256Settings
password:#"123456"
error:&error];
NSString* messageServer = [NSString base64forData:postData];
NSString* postMessage = [#"message=" stringByAppendingString:messageServer];
postData = [postMessage dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; // problem here I think
NSString* postLength = [NSString stringWithFormat:#"%ld",(unsigned long)[postData length]];
NSURL* URLToRequest = [NSURL URLWithString:address];
NSMutableURLRequest* semisystem = [[[NSMutableURLRequest alloc] initWithURL:URLToRequest] autorelease];
[semisystem setHTTPMethod:#"POST"];
[semisystem setHTTPBody:postData];
[semisystem setValue:postLength forHTTPHeaderField:#"Content-Length"];
[semisystem setValue:self.activationURL forHTTPHeaderField:#"EncryptionKey"];
[semisystem setValue:messageServer forHTTPHeaderField:#"data"];
NSURLResponse* response;
NSData* data = [NSURLConnection sendSynchronousRequest:semisystem
returningResponse:&response
error:&error];
responseString2 = [NSString stringWithFormat:#"%.*s", (int)[data length], [data bytes]];
return responseString2;
}
PHP code:
function decrypt2($b64_data,$password)
{
// back to binary
//$bin_data = mb_convert_encoding($b64_data, "UTF-8", "BASE64");
$bin_data = base64_decode($b64_data);
// extract salt
$salt = substr($bin_data, 2, 8);
// extract HMAC salt
$hmac_salt = substr($bin_data, 10, 8);
// extract IV
$iv = substr($bin_data, 18, 16);
// extract data
$data = substr($bin_data, 34, strlen($bin_data) - 34 - 32);
$dataWithoutHMAC = chr(2).chr(1).$salt.$hmac_salt.$iv.$data;
// extract HMAC
$hmac = substr($bin_data, strlen($bin_data) - 32);
// make HMAC key
$hmac_key = pbkdf2('SHA1', $password, $hmac_salt, 10000, 32, true);
// make HMAC hash
$hmac_hash = hash_hmac('sha256', $dataWithoutHMAC , $hmac_key, true);
// check if HMAC hash matches HMAC
if($hmac_hash != $hmac) {
echo "HMAC mismatch".$nl.$nl.$nl;
// return false;
}
// make data key
$key = pbkdf2('SHA1', $password, $salt, 10000, 32, true);
// decrypt
$ret = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
return $ret;
}
$passkey = "123456";
$messageBase64 = $_POST['message'];// THIS barely works
$messageBase64 = $_SERVER['HTTP_DATA'];// THIS WORKS
$message = decrypt2($messageBase64,$passkey);
Many thanks in advance!
I know this is a old question, but for a long time I used the same solution and the problem was that we are not encoding properly the url before making the request to the server. The documentation says:
According to RFC 3986, the reserved characters in a URL are:
reserved = gen-delims / sub-delims
gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "#"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
And here is how to encode the string:/
CFStringRef encodedString =
CFURLCreateStringByAddingPercentEscapes(
kCFAllocatorDefault,
(__bridge CFStringRef)(originalString),
NULL,
CFSTR(":/?#[]#!$&'()*+,;="),kCFStringEncodingUTF8);
And to get the string again:
NSString* stringEncoded = CFBridgingRelease
(CFURLCreateWithString(kCFAllocatorDefault, encodedString, NULL));
I think this is the best we can do, because we make sure that string will be properly encoded and during the request the symbols will not be replaced for other thing.
here is the references:
http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFURLRef/Reference/reference.html#//apple_ref/c/func/CFURLCreateStringByAddingPercentEscapes
http://developer.apple.com/library/ios/#documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/WorkingWithHTTPAndHTTPSRequests/WorkingWithHTTPAndHTTPSRequests.html
I've just found the solution. During the request, the '+' symbols are being interpreted as whitespaces by the server, breaking the base64 code. The following line fixed this problem:
postMessage = [postMessage stringByReplacingOccurrencesOfString:#"+" withString:#"%2B"];
you can check your encoded/decoded string, in this url
http://meyerweb.com/eric/tools/dencoder/
As you can see "+" change by "%2B" when you press encode button.