Sending multipart POST from iOS and reading parameters in PHP $_POST?

Goles picture Goles · Oct 12, 2011 · Viewed 19.4k times · Source

I'm trying to send a multi-line post from an iPhone/iPad to a php service, the problem is that for some reason, the POST Content-Type seems to be application/x-www-form-urlenconded, ( I found out this using Wireshark )

This is actually a snippet of the Wireshark POST packet capture:

    **Content-Type: application/x-www-form-urlencoded\r\n**
    Content-Length: 324\r\n
        [Content length: 324]
    Connection: keep-alive\r\n
    \r\n
    [Full request URI: http://my.server.com/mobile/tools/authentication]
Line-based text data: application/x-www-form-urlencoded
    --0xKhTmLbOuNdArY\r\n
    Content-Disposition: form-data; name="login"\r\n
    \r\n
    [email protected]\r\n
    --0xKhTmLbOuNdArY\r\n
    Content-Disposition: form-data; name="password"\r\n
    \r\n
    somepassword\r\n
    --0xKhTmLbOuNdArY\r\n
    \r\n
    --0xKhTmLbOuNdArY--\r\n

The problem is that in the server I'm trying to read the login and password variables from this POST request, but it's not possible since I think php thinks the POST request is x-www-form-urlencoded, so if I setup authentication.php to do:

<?php
echo("<pre>")
print_r($_POST)
echo("</pre>")
?>

I get this:

<pre>Array\n
(\n
    [--0xKhTmLbOuNdArY\r\n
Content-Disposition:_form-data;_name] => "login"\r\n
\r\n
[email protected]\r\n
--0xKhTmLbOuNdArY\r\n
Content-Disposition: form-data; name="password"\r\n
\r\n
somepassword\r\n
--0xKhTmLbOuNdArY\r\n
\r\n
--0xKhTmLbOuNdArY--\r\n
\n
)\n
</pre>

This is clearly not good, since If I send a request using this simple html form:

<FORM action="http://my.server.com/mobile/tools/authentication.php" method="post">
   <P>
   <LABEL for="login">E-mail: </LABEL>
             <INPUT type="text" name="login"><BR>
   <LABEL for="password">pass: </LABEL>
             <INPUT type="text" name="password"><BR>
   <INPUT type="submit" value="Send"> <INPUT type="reset">
   </P>
</FORM>

I get this Wireshark trace as expected from a form:

    Content-Type: application/x-www-form-urlencoded\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
    Accept-Encoding: gzip,deflate,sdch\r\n
    Accept-Language: en-US,en;q=0.8\r\n
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n
    Cookie: PHPSESSID=tpp10pbrdkkf5dg9qprlamfuu2\r\n
    \r\n
    [Full request URI: http://mymed2.sophia.inria.fr/mobile/tools/authentication.php]
Line-based text data: application/x-www-form-urlencoded
    login=hello%40hello.com&password=somepassword

And this text output from the print_r($_POST) in authentication.php

<pre>Array\n
(\n
    [login] => [email protected]\n
    [password] => somepassword\n
)\n
</pre>

This is the way I'm crafting the POST request in Objective-C and Cocoa

- (NSMutableURLRequest *) POSTRequestWithURL:(NSURL *)url andDataDictionary:(NSDictionary *) dictionary
{
    // Create a POST request
    NSMutableURLRequest *myMedRequest = [NSMutableURLRequest requestWithURL:url];
    [myMedRequest setHTTPMethod:@"POST"];

    // Add HTTP header info
    // Note: POST boundaries are described here: http://www.vivtek.com/rfc1867.html
    // and here http://www.w3.org/TR/html4/interact/forms.html
    NSString *POSTBoundary = [NSString stringWithString:@"0xKhTmLbOuNdArY"];
    [myMedRequest addValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@\r\n", POSTBoundary] forHTTPHeaderField:@"Content-Type"];

    // Add HTTP Body
    NSMutableData *POSTBody = [NSMutableData data];
    [POSTBody appendData:[[NSString stringWithFormat:@"--%@\r\n",POSTBoundary] dataUsingEncoding:NSUTF8StringEncoding]];

    // Add Key/Values to the Body
    NSEnumerator *enumerator = [dictionary keyEnumerator];
    NSString *key;

    while ((key = [enumerator nextObject])) {
        [POSTBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
        [POSTBody appendData:[[NSString stringWithFormat:@"%@", [dictionary objectForKey:key]] dataUsingEncoding:NSUTF8StringEncoding]];
        [POSTBody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", POSTBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
    }

    // Add the closing -- to the POST Form
    [POSTBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", POSTBoundary] dataUsingEncoding:NSUTF8StringEncoding]]; 

    // Add the body to the myMedRequest & return
    [myMedRequest setHTTPBody:POSTBody];
    return myMedRequest;
}

As you see, in the beginning I do:

    [myMedRequest addValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@\r\n", POSTBoundary] forHTTPHeaderField:@"Content-Type"];

But in the previous Wireshark trace logs, the POST packet was being viewed as Content-Type: application/x-www-form-urlencoded, maybe I'm doing something else wrong ?

Thanks :)

EDIT: I want to avoid external frameworks like ASIHTTPRequest, by the way, ASIHTTPRequest development has been abandoned by the lead developer as stated here

Answer

Goles picture Goles · Oct 23, 2011

Ok, since I couldn't find a solution without external libraries I fixed my code and open sourced it, in case anyone want's to use it.

The problem on the code I posted was the \r\n on the line

[myMedRequest addValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@\r\n", POSTBoundary] forHTTPHeaderField:@"Content-Type"];

Removing that fixed everything.

Hope this helps someone else :)