Escaping CURL # symbol with PHP - php

I'm writing a php application that submits via curl data to sign up for an iContact email list. However I keep getting an invalid email address error. I think this may be due to the fact that I'm escaping the # symbol so it looks like %40 instead of #. Also, according to the php documentation for curl_setopt with CURLOPT_POSTFIELDS:
The full data to post in a HTTP
"POST" operation. To post a file,
prepend a filename with # and use the
full path.
So, is there anyway to pass the # symbol as post data through curl in php without running it through urlencode first?

Use http_build_query() on your data-array first before passing it to curl_setopt(), that will lead to it sending the form as application/x-www-form-encoded instead of multipart/form-data (and thus the # is not interpreted).
Also why do you really care about the # in an email-address? It only matters if the # is the first character, not somewhere in the middle.

After search PHP curl manual, I found there is no information to escape the first ‘#’ if the post field is a string instead of a file if post with multipart/form-data encoding.
The way I worked around this problem is prefixing a blank at the beginning of the text. While our backend API will strip blanks so it could remove the blank and restore the original text. I don't know weather Twitter API will trim blanks on user input.
If so, this workaround also works for you.
If any one found the way to escaping the first '#' when using PHP curl with multipart/form-data encoding, please let us know.

I ran into the same issue, though with curl itself and not PHP curl.
When using curl's field option '-F' a leading # symbol will not be sent in the POST but instead will instruct curl to send the file name that immediately succeeds the symbol as part of the POST.
Fortunately, curl offers another option '--form-string', which behaves the same way as '-F', except that the 'form-string' option is not parsed.
As an example, if you want to use curl to POST field1 with value "#value" and file1 with the file "testfile.txt" you can do so as follows:
curl "http://www.url.com" --form-string "field1=#value" -F "file1=#testfile.txt"

This is the true solution that can support both string containing # and files.
Solution for PHP 5.6 or later:
Use CURLFile instead of #.
Solution for PHP 5.5 or later:
Enable CURLOPT_SAFE_UPLOAD.
Use CURLFile instead of #.
Solution for PHP 5.3 or later:
Build up multipart content body by youself.
Change Content-Type header by yourself.
The following snippet will help you :D
<?php
/**
* For safe multipart POST request for PHP5.3 ~ PHP 5.4.
*
* #param resource $ch cURL resource
* #param array $assoc "name => value"
* #param array $files "name => path"
* #return bool
*/
function curl_custom_postfields($ch, array $assoc = array(), array $files = array()) {
// invalid characters for "name" and "filename"
static $disallow = array("\0", "\"", "\r", "\n");
// initialize body
$body = array();
// build normal parameters
foreach ($assoc as $k => $v) {
$k = str_replace($disallow, "_", $k);
$body[] = implode("\r\n", array(
"Content-Disposition: form-data; name=\"{$k}\"",
"",
filter_var($v),
));
}
// build file parameters
foreach ($files as $k => $v) {
switch (true) {
case false === $v = realpath(filter_var($v)):
case !is_file($v):
case !is_readable($v):
continue; // or return false, throw new InvalidArgumentException
}
$data = file_get_contents($v);
$v = call_user_func("end", explode(DIRECTORY_SEPARATOR, $v));
list($k, $v) = str_replace($disallow, "_", array($k, $v));
$body[] = implode("\r\n", array(
"Content-Disposition: form-data; name=\"{$k}\"; filename=\"{$v}\"",
"Content-Type: application/octet-stream",
"",
$data,
));
}
// generate safe boundary
do {
$boundary = "---------------------" . md5(mt_rand() . microtime());
} while (preg_grep("/{$boundary}/", $body));
// add boundary for each parameters
array_walk($body, function (&$part) use ($boundary) {
$part = "--{$boundary}\r\n{$part}";
});
// add final boundary
$body[] = "--{$boundary}--";
$body[] = "";
// set options
return curl_setopt_array($ch, array(
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => implode("\r\n", $body),
CURLOPT_HTTPHEADER => array(
"Expect: 100-continue",
"Content-Type: multipart/form-data; boundary={$boundary}", // change Content-Type
),
));
}
?>

#PatricDaryll's answer is correct, but I needed to make a bit of researches to understand where to use this http_build_query function.
To clarify and summarise, instead of doing:
curl_setopt($ch, CURLOPT_POSTFIELDS, $array);
You will use:
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($array));
Simple but confusing... curl is smart enough to understand if you gave him a string or an array.

In order to escape the # sign in the non-file data, you need to do the following.
Prepend the text string with the NULL character
$postfields = array(
'upload_file' => '#file_to_upload.png',
'upload_text' => sprintf("\0%s", '#text_to_upload')
);
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'http://example.com/upload-test');
curl_setopt($curl, CURLOPT_POSTFIELDS, $postfields);
curl_exec($curl);
curl_close($curl);

Related

Pass whole incoming data to curl - Laravel

I wanted to pass the whole incoming data (that is, $request) to the curl not wanted to post to a particular field in the endpoint as subjectId=>1 as am running this curl request for different endPoint everytime. The below curl request will work if CURLOPT_URL => $url . $subjectId, was given. As my input changes for every end point, i've to pass everything that comes in the input to the curl , i can't pass it as an arary $subjectId. Is there any way to do this?
Currently, dd($Response); returns null
Am giving a postman input like this:
{
"subjectId":"1"
}
Curl
public function getContentqApiPost(Request $request)
{
$token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ey";
$headers = [
"Accept: application/json",
"Authorization: Bearer " . $token
];
$url="http://127.0.0.1:9000/api/courses/course-per-subject";
$subjectId = "?subjectId=$request->subjectId";
$ch = curl_init();
$curlConfig = array(
// CURLOPT_URL => $url . $subjectId,
CURLOPT_URL => $url . $request,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt_array($ch, $curlConfig);
$result = trim(curl_exec($ch));
$Response = json_decode($result, true);
if (curl_errno($ch)) {
$error_msg = curl_error($ch);
echo $error_msg;
}
curl_close($ch);
return $Response;
}
If you would like to pass all params of $request to curl:
$queryParams = '';
$delimeter = '?';
foreach($request->all() as $k => $v){
$queryParams .= "$delimeter$k=$v";
$delimeter = '&';
}
Also You can only pass the params you want:
foreach($request->only(['subjectId']) as $k => $v){
// code here
}
Finally you have:
CURLOPT_URL => $url . $queryParams,
Answer
Assuming you want to pass the entire GET query string as-is:
$query_string = str_replace($request->url(), "", $request->fullUrl());
$url = "http://localhost:9000/api/courses/course-per-subject" . $query_string;
This works because $request->url() returns the URL without the query string parameters, while $request->fullUrl() returns the URL with all the query string parameters, so we can use str_replace with an empty replacement to remove the non-query part. Note that $query_string will already start with a ? so there is no need to add that yourself.
Other suggestions
Unless your Laravel API is a 1:1 copy of the backend API, I strongly suggest writing a class that interfaces with the backend API, then provide it to your Laravel controllers using dependency injection. E.g.
class CourseCatalogApi {
public function getSubjectsInCourse(String $course){
... // your curl code here
}
}
Finally, since you are already using Laravel, there is no need to write such low level code using curl to make HTTP requests. Consider using guzzlehttp, which is already a dependency of Laravel.

get some funny charactres in json text

I am calling a web service that returns json text which ends up with some garbage at the start "". Any help or pointers appreciated. I am a bit rusty with the curl options and this from some old code i have used, it has been some time since I have done work like this.
When i call the web service through the browser i get nice json text, such as following. I have removed some of the values to make only a few lines
{ "values": [[1511596680,3],[1511596740,2],[1511596800,0],[1511596860,6],[1511596920,0],[1511596980,0],[1511597040,0],[1511597100,0],[1511597160,0],[1511603220,0],[1511603280,0],[1511603340,0],[1511603400,0],[1511603460,0],[1511603520,0],[1511603580,0],[1511603640,0],[1511603700,0],[1511603760,0],[1511603820,0]]}
when i call via a php page that acts as a wrapper. i get some crap in front of it, which is preventing php from calling json_decode on it. The called url is exactly the same that i used previously to call the web service in the browser.
{ "values": [[1511596680,3],[1511596740,2],[1511596800,0],[1511596860,6],[1511596920,0],[1511596980,0],[1511597040,0],[1511597100,0],[1511597160,0],[1511603220,0],[1511603280,0],[1511603340,0],[1511603400,0],[1511603460,0],[1511603520,0],[1511603580,0],[1511603640,0],[1511603700,0],[1511603760,0],[1511603820,0]]}
my php code to call the web service is as follows. I am not sure if $post_string being empty is a problem. The url consists of params passed in a url string in form ?param=val&param2=val2 etc.
$contenttype = 'application/json';
$headers = array(
'Content-Type: ' . $contenttype,
'Content-Length: ' . strlen($post_string) /* this an empty string */
);
/* dump of headers
Array
(
[0] => Content-Type: application/json
[1] => Content-Length: 0
)
*/
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); // this is get */
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
if (is_array($headers)
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$output = curl_exec($ch); // this contains the crap at the start */
i had insert the following to remive the Byte Order Mark.
$output = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $output);
thanks to the following link
How do I remove  from the beginning of a file?
the "funny characters" are caused by the UTF-8 BOM, that means the string starts with EF BB BF signaling that it was encoded in UTF-8.
you can remove the BOM like this: (found in another answer, by jasonhao):
//Remove UTF8 Bom
function remove_utf8_bom($text)
{
$bom = pack('H*','EFBBBF');
$text = preg_replace("/^$bom/", '', $text);
return $text;
}

oAuth signature creation issue with PHP (posting photoset to Tumblr)

I've made a simple script that posts images on tumblr.
everything is fine, but I've noticed some performance issues right after I've changed the host provider (my new host is limited and cheaper).
now, after debugging the script and after contacting the tumblr api helpdesk, I'm stuck on a problem:
there are 3 functions:
function oauth_gen($method, $url, $iparams, &$headers) {
$iparams['oauth_consumer_key'] = CONSUMER_KEY;
$iparams['oauth_nonce'] = strval(time());
$iparams['oauth_signature_method'] = 'HMAC-SHA1';
$iparams['oauth_timestamp'] = strval(time());
$iparams['oauth_token'] = OAUTH_TOKEN;
$iparams['oauth_version'] = '1.0';
$iparams['oauth_signature'] = oauth_sig($method, $url, $iparams);
$oauth_header = array();
foreach($iparams as $key => $value) {
if (strpos($key, "oauth") !== false) {
$oauth_header []= $key ."=".$value;
}
}
$str = print_r($iparams, true);
file_put_contents('data1-1.txt', $str);
$oauth_header = "OAuth ". implode(",", $oauth_header);
$headers["Authorization"] = $oauth_header;
}
function oauth_sig($method, $uri, $params) {
$parts []= $method;
$parts []= rawurlencode($uri);
$iparams = array();
ksort($params);
foreach($params as $key => $data) {
if(is_array($data)) {
$count = 0;
foreach($data as $val) {
$n = $key . "[". $count . "]";
$iparams []= $n . "=" . rawurlencode($val);
//$iparams []= $n . "=" . $val;
$count++;
}
} else {
$iparams[]= rawurlencode($key) . "=" .rawurlencode($data);
}
}
//debug($iparams,"iparams");
$str = print_r($iparams, true);
file_put_contents('data-1.txt', $str);
//$size = filesize('data.txt');
$parts []= rawurlencode(implode("&", $iparams));
//debug($parts,"parts");
//die();
$sig = implode("&", $parts);
return base64_encode(hash_hmac('sha1', $sig, CONSUMER_SECRET."&". OAUTH_SECRET, true));
}
these 2 functions above comes from an online functional example, they have always worked fine.
this is the function I use to call the APIs and the oAuth:
function posta_array($files,$queue,$tags,$caption,$link,$blog){
$datArr = array();
$photoset_layout = "";
foreach ($files as $sing_file){
$dataArr [] = file_get_contents($sing_file);
$photoset_layout .= "1";
}
$headers = array("Host" => "http://api.tumblr.com/", "Content-type" => "application/x-www-form-urlencoded", "Expect" => "");
$params = array(
"data" => $dataArr,
"type" => "photo",
"state" => $queue,
"tags"=>$tags,
"caption"=>$caption,
"photoset_layout" => $photoset_layout,
"link"=>str_replace("_","",$link)
);
debug($headers,"head");
oauth_gen("POST", "http://api.tumblr.com/v2/blog/$blog/post", $params, $headers);
debug($headers,"head 2");
$ch = curl_init();
curl_setopt($ch, CURLOPT_USERAGENT, "Tumblr v1.0");
curl_setopt($ch, CURLOPT_URL, "http://api.tumblr.com/v2/blog/$blog/post");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Authorization: " . $headers['Authorization'],
"Content-type: " . $headers["Content-type"],
"Expect: ")
);
$params = http_build_query($params);
$str = print_r($params, true);
file_put_contents('data_curl1.txt', $str);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
$response = curl_exec($ch);
debug($response,"response");
return $response;
}
this is the function with some problems, I try to explain:
I called the oauth_gen passing the parameters array to it, the oauth_gen creates the oauth header that I later used here: "Authorization: " . $headers['Authorization'],.
As I stated, everything is working smoothly, until I have tried to post a gif photoset of 6 files for a total of 6Mb (tumblr permit 2Mb each file and 10Mb total).
PHP runs out of memory and return an error, here it starts my debugging, after a while I contacted the tumblr api helpdesk, and they answer in this way:
You shouldn't need to include the files in the parameters used for
generating the oauth signature. For an example of how this is done,
checkout one of our official API clients.
This changes everything. Untill now, I passed the entire parameters array to the oauth_gen, which, calling the oauth_sig, will rawencode everything into the array (binary strings of gif files inlcuded), with a result of a binary file of about 1Mb becomes at least 3Mb of rawurlencoded string.
and that's why I had memory issues. Nice, so, as the helpdesk say, I've changed the call to the oauth_gen in this way:
$new_array = array();
oauth_gen("POST", "http://api.tumblr.com/v2/blog/$blog/post", $new_array, $headers);
seams legit to me, I passed a new array to the function, the function then generate the oAuth, the headers are passed back and I can use them into the posting call, the result was:
{"meta":{"status":401,"msg":"Unauthorized"},"response":[]}
asking more to tumblr api helpdesk leads only to more links to their documentation and their "tumblr php client" which I can't use, so it isn't a option.
Does anyone has experience with oAuth and can explain me what I'm doing wrong? as far as I understand, the trick is into the encrypted data the oauth_sig create, but I can't figure out how to proceed.
I really want to understand the oauth, but more I read about it and more the tumblr helpdsek seams right to me, but... the solution doesn't work, and works only if I let the oauth function to encrypt the entire data array (with the images and everything) but I can understand that this is wrong... help me.
UPDATE 1
I've tried a new thing today, first I created the empty array, then passed by reference to the oauth_genand only after generating the signature, I've added to the same array all the other fields about the post itself, but the result is the same.
UPDATE 2
reading here: http://oauth.net/core/1.0a/#signing_process
seems that the parameters of the request must all be used for the signature, but this is not totally clear (if someone could explain it better, I really appreciate).
this is weird, because if it's true, it go against the words of the Tumblr help desk, while if it's not true, there is a little confusion in the whole process.
by the way, at this time, I'm stile struck in the same point.
After digging couple of hours into the issue, debugging, reviewing tumblr api and api client, registering a test account and trying to post some images. The good news is finally I come up with a solution. It is not using a native CURL only, you need guzzle and an OAuth library to sign the requests.
Tumblr guys are correct about signing the request. You don't need to pass image data to sign the request. If you check their official library you can see; https://github.com/tumblr/tumblr.php/blob/master/lib/Tumblr/API/RequestHandler.php#L85
I tried to fix the issue with native CURL library but unfortunately I was not successful, either I was signing the request in a wrong way or missing something in the request header, data etc. I don't know actually, Tumblr api is really bad at informing you what you are doing wrong.
So I cheated a little bit and start to read Tumblr api client code, and I come up with a solution.
Here we go, first you need two packages.
$ composer require "eher/oauth:1.0.*"
$ composer require "guzzle/guzzle:>=3.1.0,<4"
And then the PHP code, just define your keys, tokens, secrets etc. Then it should be good to go.
Since the signing request does not include picture data, it is not exceeding memory limit. After signing the request actually we are not getting the contents of the files into our post data array. We are using addPostFiles method of guzzle, which takes care of file addition to POST request, does the dirty work for you. And here is the result for me;
string(70) "{"meta":{"status":201,"msg":"Created"},"response":{"id":143679527674}}"
And here is the url;
http://blog-transparentcoffeebouquet.tumblr.com/
<?php
ini_set('memory_limit', '64M');
define("CONSUMER_KEY", "");
define("CONSUMER_SECRET", "");
define("OAUTH_TOKEN", "");
define("OAUTH_SECRET", "");
function request($options,$blog) {
// Take off the data param, we'll add it back after signing
$files = isset($options['data']) ? $options['data'] : false;
unset($options['data']);
$url = "https://api.tumblr.com/v2/blog/$blog/post";
$client = new \Guzzle\Http\Client(null, array(
'redirect.disable' => true
));
$consumer = new \Eher\OAuth\Consumer(CONSUMER_KEY, CONSUMER_SECRET);
$token = new \Eher\OAuth\Token(OAUTH_TOKEN, OAUTH_SECRET);
$oauth = \Eher\OAuth\Request::from_consumer_and_token(
$consumer,
$token,
"POST",
$url,
$options
);
$oauth->sign_request(new \Eher\OAuth\HmacSha1(), $consumer, $token);
$authHeader = $oauth->to_header();
$pieces = explode(' ', $authHeader, 2);
$authString = $pieces[1];
// POST requests get the params in the body, with the files added
// and as multipart if appropriate
/** #var \Guzzle\Http\Message\RequestInterface $request */
$request = $client->post($url, null, $options);
$request->addHeader('Authorization', $authString);
if ($files) {
if (is_array($files)) {
$collection = array();
foreach ($files as $idx => $f) {
$collection["data[$idx]"] = $f;
}
$request->addPostFiles($collection);
} else {
$request->addPostFiles(array('data' => $files));
}
}
$request->setHeader('User-Agent', 'tumblr.php/0.1.2');
// Guzzle throws errors, but we collapse them and just grab the
// response, since we deal with this at the \Tumblr\Client level
try {
$response = $request->send();
} catch (\Guzzle\Http\Exception\BadResponseException $e) {
$response = $request->getResponse();
}
// Construct the object that the Client expects to see, and return it
$obj = new \stdClass;
$obj->status = $response->getStatusCode();
$obj->body = $response->getBody();
$obj->headers = $response->getHeaders()->toArray();
return $obj;
}
$files = [
"/photo/1.jpg",
"/photo/2.jpg",
"/photo/3.png",
"/photo/4.jpg",
"/photo/1.jpg",
"/photo/2.jpg",
"/photo/3.png",
"/photo/4.jpg",
"/photo/1.jpg",
"/photo/2.jpg",
];
$params = array(
"type" => "photo",
"state" => "published",
"tags"=> [],
"caption"=>"caption",
"link"=>str_replace("_","","http://stackoverflow.com/questions/36747697/oauth-signature-creation-issue-with-php-posting-photoset-to-tumblr"),
"data" => $files,
);
$response = request($params, "blog-transparentcoffeebouquet.tumblr.com");
var_dump($response->body->__toString());

How to use file_get_contents() with a Wordpress user's cookies

I need to send a file_get_contents() to an API endpoint with the client's cookies that are set by Wordpress to show that the user is logged into the wordpress site. I know I need to use stream_context_create() roughly as follows:
$cookies = ??? //THIS IS THE QUESTION (see answer below)!
// Create a stream
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: en\r\n" .
"Cookie: {$cookies}\r\n"
)
);
$context = stream_context_create($opts);
// Open the file using the HTTP headers set above
$file = file_get_contents('http://example.dev/api/autho/', false, $context);
As you can see from the comment on the first line, I'm stuck on how to send this request so that the correct cookies are sent. I know the correct cookies are sent because I can print out $_COOKIES and see them there. But if I try to insert that same array into the headers, it doesn't work.
Thanks in advance!
ps: I've read that I should use cURL for this, but I'm not sure why and I don't know how to use it... but I'm open to the idea.
UPDATE:
I got this to work. It's basically the same thing I was doing, with another important cookie . See my answer below.
The cookies should be in the following format: Cookie: cookieone=value; cookietwo=value, that is, separated by a semicolon and space with no trailing semicolon. Loop through your cookie array, output that format, and send it.
It turns out I was doing it correctly, but I didn't know that WP needs a second cookie sent in order for the request to work properly.
Here's the code that works for me:
$cookies = $_COOKIE;
$name;
$value;
foreach ($_COOKIE as $key => $cookie ) {
if ( strpos( $key, 'wordpress_logged_in') !== FALSE ) {
$name = $key;
$value = $cookie;
}
}
// Create a stream
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: en\r\n" .
"Cookie: {$key}={$cookie}; wordpress_test_cookie=WP Cookie check \r\n"
)
);
$context = stream_context_create($opts);
// Open the file using the HTTP headers set above
$file = file_get_contents('http://mydomain.dev/api/autho/', false, $context);
var_dump($file);
It's basically the same thing as you see in my question, but with an important addition: wordpress_test_cookie=WP Cookie check. I haven't seen it documented anywhere, but WP needs this cookie as well as the actual wordpress_logged_in cookie in order for the call to happen as an logged in user.
Okay, as you mentioned you should use cURL (partially it's my personal opinion, I have some bad experiences with server configuration that prohibited URL file wrappers).
A quote from manual:
A URL can be used as a filename with this function if the fopen
wrappers have been enabled.
So it may happen to you that the code just won't work. On the other hand cURL is designed for fetching of remote content and provides lot of control on what's going on, how to fetch data and so on.
When you look at curl_setopt you can see how many and how detailed things you can set (but you don't have to, it's just optional when you need it).
Here's the first link after googling php curl set cookies, that's great place for you to start... Basic examples are totally trivial.
$cookies = $_COOKIE;
foreach ($_COOKIE as $key => $cookie ) {
if ( strpos( $key, 'wordpress_logged_in') !== FALSE ) {
$name = $key;
$value = $cookie;
break;
}
}
// Create a stream
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: en\r\n" .
"Cookie: {$key}={$cookie}; wordpress_test_cookie=WP Cookie check\r\n"
)
);
$context = stream_context_create($opts);
// Open the file using the HTTP headers set above
$file = file_get_contents('http://mydomain.dev/api/autho/', false, $context);
var_dump($file);
I didn't have points to make a comment, so I readded the code from emersonthis. In order for this to work under my configuration (php 7.0.3, wordpress 4.4.2) I HAD TO remove the last space after the "WP Cookie check" string.

Posting with PHP and Curl, deep array

I'm trying to post via curl, I've been using the same code over and over again with no problem but now I need to be able to use an array for posts (i'm not sure if there's a proper term for that?).
I should clarify that it's specifically a file i'm trying to post, but I can't get it working with a string either so I don't think it's too do with that.
This is absouletly fine:
$uploadData = array();
$uploadData['uploads'] = "#".$file;
$uploadData['iagree'] = 'on';
This doesn't appear to work:
$uploadData = array();
$uploadData['uploads'][0] = "#".$file;
$uploadData['iagree'] = 'on';
In the second example i'm trying to replicate an input with the attribute name="uploads[]"
Obviously i'm trying to curl an external site, but if I experiment curling a page on my own server so that I can see what's being sent, I can see that the uploads array is being converted to a string:
print_r($_POST);
print_r($_FILES);
returns:
Array
(
[uploads] => Array
[iagree] => on
)
Array
(
)
This is my full Curl:
$uploadData = array();
$uploadData['uploads'][] = "#".$file;
$uploadData['iagree'] = 'on';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $theLink);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $uploadData);
$upload_response = curl_exec($ch);
curl_close($ch);
I've tried to give as much information as possible, but if i've missed something feel free to ask and i'll provide more.
Other than that, does anyone have any suggestions or solutions?
$uploadData['uploads[]'] = "#".$file; and passing it as an array should work, just keep in mind you need the absolute path to the file.
There is no mechanism in 'simple' HTTP (multipart/form-data or application/x-www-form-urlencoded) to send 'arrays'. However, PHP interprets the [ & ] characters in key-value pairs as special. PHP is alone in that AFAIK, it's not a HTTP mechanism, it's just the parsing of input PHP does, as is replacing .'s in the name of values with _. Curl is a 3rd party package which lives seperately from PHP, and as such does not understand multidimensional arrays.
Try passing the query string:
$uploadData = 'uploads[]=#' . $file . '&iagree=on&uploads[]=#' . $file2;
See if that works for you.
EDIT
Reading through the manual, the string needs to be urlencoded, try this:
$uploadData = urlencode('uploads[]=#' . $file . '&iagree=on&uploads[]=#' . $file2);
Received this information from the curl_setopt() man page:
Note:
Passing an array to CURLOPT_POSTFIELDS will encode the data as multipart/form-data, while passing a URL-encoded string will encode the data as application/x-www-form-urlencoded.
I may have used the urlencode improperly, try this:
$uploadData = 'uploads[]=' . urlencode('#' . $file) . '&iagree=' . urlencode('on') . '&uploads[]=' . urlencode('#' . $file2);
UPDATE
Ok this is my last shot at it. Reading through some user comments at the curl page I found something about serializing the sub-array. So:
$uploadData = array('iagree' => 'on', 'uploads' => serialize(array('#' . $file)));
Hopefully that is the key. If that does not work...well it may not be possible to do.
Give that a shot and see if it works. (Sorry for the trial and error, I do not have a method to test it!)

Categories