I'm trying to develop a full PHP web browser that can handle cookies. I've created the following class:
<?php
class Browser
{
private $cookies = '';
private $response_cookies = '';
private $content = '';
/**
* Cookie manager
* #Description : To set or get cookies as Array or String
*/
public function set_cookies_json($cookies)
{
$cookies_json = json_decode($cookies, true);
$cookies_array = array();
foreach ($cookies_json as $key => $value)
{
$cookies_array[] = $key .'='.$value;
}
$this->cookies = 'Cookie: ' . $cookies_array.join('; ') . "\r\n";
}
public function set_cookies_string($cookies)
{
$this->cookies = 'Cookie: ' . $cookies . "\r\n";
}
private function get_cookies()
{
global $http_response_header;
$cookies_array = array();
foreach($http_response_header as $s)
{
if (preg_match('|^Set-Cookie:\s*([^=]+)=([^;]+);(.+)$|', $s, $parts))
{
$cookies_array[] = $parts[1] . '=' . $parts[2];
}
}
$this->cookies = 'Cookie: ' . $cookies_array.join('; ') . "\r\n";
}
/**
* GET and POST request
* Send a GET or a POST request to a remote URL
*/
public function get($url)
{
$opts = array(
'http' => array(
'method' => 'GET',
'header' => "Accept-language: en\r\n" .
$this->cookies
)
);
$context = stream_context_create($opts);
$this->content = file_get_contents($url, false, $context);
$this->get_cookies();
return $this->content;
}
public function post($url, $post_data)
{
$post_content = array();
foreach ($post_data as $key => $value)
{
$post_content[] = $key .'='.$value;
}
$opts = array(
'http' => array(
'method' => 'GET',
'header' => "Content-type: application/x-www-form-urlencoded\r\n" .
$this->cookies,
'content' => $post_content.join('&'),
)
);
$context = stream_context_create($opts);
$this->content = file_get_contents($url, false, $context);
$this->get_cookies();
return $this->content;
}
}
Basically, it can send a GET request and 'should' retrieve the cookies.
I've made a very simple test script:
<?php
require('browser.class.php');
$browser = new Browser();
$browser->get('http://google.com');
print_r($browser->response_cookies);
But it fails on line 32 as $http_response_header looks to be null. Isn't it supposed to contain the header of my response ? I've read that page but it looks to work well for this guy : get cookie with file_get_contents in PHP
I know I could use cUrl to handle this but I'd really like to use a rough PHP code.
Did I make something wrong ?
Thanks for your precious help.
Edit:
Solution is:
<?php
class Browser
{
public $request_cookies = '';
public $response_cookies = '';
public $content = '';
/**
* Cookie manager
* #Description : To set or get cookies as Array or String
*/
public function set_cookies_json($cookies)
{
$cookies_json = json_decode($cookies, true);
$cookies_array = array();
foreach ($cookies_json as $key => $value)
{
$cookies_array[] = $key .'='.$value;
}
$this->request_cookies = 'Cookie: ' . join('; ', $cookies_array) . "\r\n";
}
public function set_cookies_string($cookies)
{
$this->request_cookies = 'Cookie: ' . $cookies . "\r\n";
}
private function get_cookies($http_response_header)
{
$cookies_array = array();
foreach($http_response_header as $s)
{
if (preg_match('|^Set-Cookie:\s*([^=]+)=([^;]+);(.+)$|', $s, $parts))
{
$cookies_array[] = $parts[1] . '=' . $parts[2];
}
}
$this->response_cookies = 'Cookie: ' . join('; ', $cookies_array) . "\r\n";
}
/**
* GET and POST request
* Send a GET or a POST request to a remote URL
*/
public function get($url)
{
$opts = array(
'http' => array(
'method' => 'GET',
'header' => "Accept-language: en\r\n" .
$this->request_cookies
)
);
$context = stream_context_create($opts);
$this->content = file_get_contents($url, false, $context);
$this->get_cookies($http_response_header);
return $this->content;
}
public function post($url, $post_data)
{
$post_content = array();
foreach ($post_data as $key => $value)
{
$post_content[] = $key .'='.$value;
}
$opts = array(
'http' => array(
'method' => 'GET',
'header' => "Content-type: application/x-www-form-urlencoded\r\n" .
$this->request_cookies,
'content' => join('&', $post_content),
)
);
$context = stream_context_create($opts);
$this->content = file_get_contents($url, false, $context);
$this->get_cookies($http_response_header);
return $this->content;
}
}
According to php.net:
$http_response_header will be created in the local scope.
Because of this $http_response_header is available only in the scope of get().
You can pass it like $this->get_cookies($http_response_header); or create a property to store it.
Related
I am using php file_get_contents and dom documment for login to 3rd party site (gmail). But when i run my code, it's displaying cookie problem text page: Turn cookies on or off, that's mean i am not enable cookie. But already i turn it on using this code $http_response_header, check below code then you will understand.
I don't want to use curl. Curl support cookies but file_get_contents not supporting cookies. I want to remote login using only php, so i don't used curl. I don't understand exactly where is my mistake and why displaying cookie problem after adding cookie function on code. After unsuccessful, i submitting this post on here for getting solution from genius.
Here Is My gmail.php Code:
<?php
class LoginGmail
{
public $request_cookies = '';
public $response_cookies = '';
public $content = '';
public function set_cookies_json($cookies)
{
$cookies_json = json_decode($cookies, true);
$cookies_array = array();
foreach ($cookies_json as $key => $value)
{
$cookies_array[] = $key .'='.$value;
}
$this->request_cookies = 'Cookie: ' . join('; ', $cookies_array) . "\r\n";
}
public function set_cookies_string($cookies)
{
$this->request_cookies = 'Cookie: ' . $cookies . "\r\n";
}
private function get_cookies($http_response_header)
{
$cookies_array = array();
foreach($http_response_header as $s)
{
if (preg_match('|^Set-Cookie:\s*([^=]+)=([^;]+);(.+)$|', $s, $parts))
{
$cookies_array[] = $parts[1] . '=' . $parts[2];
}
}
$this->response_cookies = 'Cookie: ' . join('; ', $cookies_array) . "\r\n";
}
public function get($url)
{
$opts = array(
'http' => array(
'method' => 'GET',
'header' => "Accept-language: en\r\n" .
"User-Agent: com.google.android.apps.maps/984200142 (Linux; U; Android 5.0; en_US; GT-I9500; Build/LRX21M; Cronet/66.0.3359.100)\r\n" .
$this->request_cookies
)
);
$context = stream_context_create($opts);
$this->content = file_get_contents($url, false, $context);
$this->get_cookies($http_response_header);
return $this->content;
}
public function post($url, $inputs)
{
$post_content = array();
foreach ($inputs as $key => $value)
{
$post_content[] = $key .'='.$value;
}
$opts = array(
'http' => array(
'method' => 'POST',
'header' => "Content-type: application/x-www-form-urlencoded\r\n" .
"User-Agent: com.google.android.apps.maps/984200142 (Linux; U; Android 5.0; en_US; GT-I9500; Build/LRX21M; Cronet/66.0.3359.100)\r\n" .
$this->request_cookies,
'content' => join('&', $post_content),
)
);
$context = stream_context_create($opts);
$this->content = file_get_contents($url, false, $context);
$this->get_cookies($http_response_header);
return $this->content;
}
public function postPass($url, $inputs)
{
$post_content = array();
foreach ($inputs as $key => $value)
{
$post_content[] = $key .'='.$value;
}
$opts = array(
'http' => array(
'method' => 'POST',
'header' => "Content-type: application/x-www-form-urlencoded\r\n" .
"User-Agent: com.google.android.apps.maps/984200142 (Linux; U; Android 5.0; en_US; GT-I9500; Build/LRX21M; Cronet/66.0.3359.100)\r\n" .
$this->request_cookies,
'content' => join('&', $post_content),
)
);
$context = stream_context_create($opts);
$this->content = file_get_contents($url, false, $context);
$this->get_cookies($http_response_header);
return $this->content;
}
}
?>
And Here Is My login.php Code:
<?php
require_once('gmail.php');
$connect = new LoginGmail();
$url = 'https://store.google.com/account';
$first = $connect->get($url);
$domd = #DOMDocument::loadHTML($first);
$xp = new DOMXPath($domd);
foreach($domd->getElementsByTagName("form")->item(0)->getElementsByTagName("input") as $get)
{
$inputs[$get->getAttribute("name")]=$get->getAttribute("value");
}
$inputs["Email"]="HERE_IS_GMAIL_ID";
$url = $xp->query("//form")->item(0)->getAttribute("action");
$second = $connect->post($url, $inputs);
$domd = #DOMDocument::loadHTML($second);
$xp = new DOMXPath($domd);
foreach($domd->getElementsByTagName("form")->item(0)->getElementsByTagName("input") as $get)
{
$inputs[$get->getAttribute("name")]=$get->getAttribute("value");
}
$inputs["Passwd"]="HERE_IS_GMAIL_PASSWORD";
$url = $xp->query("//form")->item(0)->getAttribute("action");
echo $connect->postPass($url, $inputs);
?>
NB: I hide my personal Email & Password on code. Sorry for not good english. Thanks
I believe that the way you are performing requests on google is not the correct form. Your code is not wrong, the way you try to authenticate yourself. Check out this documentation, maybe it will help.
I've been trying to get a PHP application to connect to Instapaper's Xauth services, but for the life of me I can't get it to work. I keep getting an "403: Invalid signature." error.
The error says that my signature base string wasn't what it expected, but when I compare the signature base string I construct with what they say they expect, they're exactly the same (sensitive information removed):
My signature base string:
POST&https%3A%2F%2Fwww.instapaper.com%2Fapi%2F1%2Foauth%2Faccess_token&oauth_callback%3DMy_URL%26oauth_consumer_key%3DCONSUMER_KEY%26oauth_nonce%3Dfe379af261aca07d890d2cfaa0f19ce0%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1461898452%26oauth_version%3D1.0%26x_auth_mode%3Dclient_auth%26x_auth_password%3DPASSWORD%26x_auth_username%3DEXAMPLE%2540gmail.com
What the error says it expects:
POST&https%3A%2F%2Fwww.instapaper.com%2Fapi%2F1%2Foauth%2Faccess_token&oauth_callback%3DMy_URL%26oauth_consumer_key%3DCONSUMER_KEY%26oauth_nonce%3Dfe379af261aca07d890d2cfaa0f19ce0%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1461898452%26oauth_version%3D1.0%26x_auth_mode%3Dclient_auth%26x_auth_password%3DPASSWORD%26x_auth_username%3DEXAMPLE%2540gmail.com
I pulled my php library from https://github.com/mheap/Instapaper-XAuth-PHP, but it's old so I've tried to modify it to work with the current Instapaper API. I believe I'm generating the signature string correctly and am following the instructions found here: https://dev.twitter.com/oauth/overview/creating-signatures and here: http://oauthbible.com/
I don't know what's wrong with the code, can someone please help?
class XAuth_Connection
{
private $_headers = array(
"oauth_signature_method" => "HMAC-SHA1",
"oauth_version" => "1.0",
"oauth_callback" => "MY_URL",
"oauth_consumer_key" => "",
"oauth_nonce" => "",
"oauth_timestamp" => ""
);
private $_params = array(
"x_auth_mode" => "client_auth",
"x_auth_username" => "",
"x_auth_password" => ""
);
private $_access_url = '';
public function __construct($key, $private, $access_url)
{
$this->_headers['oauth_consumer_key'] = $key;
$this->_headers['oauth_nonce'] = md5(uniqid(rand(), true));
$this->_headers['oauth_timestamp'] = time();
$this->_oauth_consumer_private = $private;
$this->_access_url = $access_url;
}
public function set_credentials($user, $password)
{
$this->_params['x_auth_username'] = $user;
$this->_params['x_auth_password'] = $password;
}
public function get_params_as_string()
{
ksort($this->_params);
$req = array();
foreach ($this->_params as $k => $v)
{
$req[] = $k ."=". $this->encode($v);
}
return implode("&", $req);
}
public function get_headers_as_string()
{
ksort($this->_headers);
$req = array();
foreach ($this->_headers as $k => $v)
{
$req[] = $k . "=" . $this->encode($v);
}
return implode("&", $req);
}
public function generate_signature()
{
//combine the parameters, encode, and sort them
$temp_params = array_merge($this->_params, $this->_headers);
$encoded_params = Array();
foreach($temp_params as $k => $v){
$encoded_params[$this->encode($k)] = $this->encode($v);
}
ksort($encoded_params);
//Build the param string
$param_base_string = "";
foreach($encoded_params as $k => $v){
$param_base_string .= $k .'='. $v . '&';
}
$param_base_string = rtrim($param_base_string, '&');
//create the signature base
$signature_base = 'POST&' . $this->encode($this->_access_url) .'&'. $this->encode($param_base_string);
$key = $this->encode($this->_oauth_consumer_private) . '&';
return base64_encode(hash_hmac("sha1",$signature_base, $key, true));
}
public function login()
{
$this->_headers['oauth_signature'] = $this->generate_signature();
ksort($this->_headers);
$header_str = 'OAuth ';
foreach ($this->_headers as $k => $v)
{
$header_str .= $k.'="'.$this->encode($v).'", ';
}
$header_str = rtrim($header_str, ', ');
$oauth_str = $this->get_params_as_string();
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->_access_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $oauth_str);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: " . $header_str));
$exec = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
if ($info['http_code'] != 200)
{
return false;
}
parse_str($exec, $r);
return $r;
}
private function encode($s)
{
return ($s === false ? $s : str_replace('%7E','~',rawurlencode($s)));
}
}
I use the file_get_contents() from server M to get the response from server X. The result was success but it take too long.
$url = "http://10.20.30.40";
$opts = array('http' =>
array(
'method' => 'GET',
'header' => 'Connection: close\r\n'
)
);
$context = stream_context_create($opts);
$result = file_get_contents($url, false, $context);
$result = json_decode($result);
$response = parse_http_response_header($http_response_header);
print_r($result);
print_r($response);
/////// below is just function to parse the response ///////
function parse_http_response_header(array $headers)
{
$responses = array();
$buffer = NULL;
foreach ($headers as $header)
{
if ('HTTP/' === substr($header, 0, 5))
{
// add buffer on top of all responses
if ($buffer) array_unshift($responses, $buffer);
$buffer = array();
list($version, $code, $phrase) = explode(' ', $header, 3) + array('', FALSE, '');
$buffer['status'] = array(
'line' => $header,
'version' => $version,
'code' => (int) $code,
'phrase' => $phrase
);
$fields = &$buffer['fields'];
$fields = array();
continue;
}
list($name, $value) = explode(': ', $header, 2) + array('', '');
// header-names are case insensitive
$name = strtoupper($name);
// values of multiple fields with the same name are normalized into
// a comma separated list (HTTP/1.0+1.1)
if (isset($fields[$name]))
{
$value = $fields[$name].','.$value;
}
$fields[$name] = $value;
}
unset($fields); // remove reference
array_unshift($responses, $buffer);
return $responses;
}
Is there any suggestion or function option to get the response (the content and the response code) faster?
(NOTE: I am not allowed to install cURL, so please gimme other option)
I want to get the profile info after getting the token
<?php
session_start ();
if (!$_SESSION['linkedin_access_token'])
{
echo 'Grant access first';
exit ();
}
## step 0
define ('LINKEDIN_KEY', 'xxxxxx');
define ('LINKEDIN_SECRET', 'xxxxxxx');
function urlencode_oauth ($str)
{
return str_replace ('+', ' ', str_replace ('%7E', '~', rawurlencode ($str)));
}
$links = array (
'request_token' => 'https://api.linkedin.com/uas/oauth/requestToken',
'authorize' => 'https://www.linkedin.com/uas/oauth/authorize',
'access_token' => 'https://api.linkedin.com/uas/oauth/accessToken',
);
## step 2
$params = array (
'oauth_consumer_key' => LINKEDIN_KEY,
'oauth_nonce' => sha1 (microtime ()),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_timestamp' => time (),
'oauth_token' => $_SESSION ['linkedin_access_token'],
'oauth_version' => '1.0'
);
## step 3
// sort parameters according to ascending order of key
// sort parameters according to ascending order of key
ksort ($params);
// prepare URL-encoded query string
$q = array ();
foreach ($params as $key => $value)
{
$q [] = urlencode_oauth ($key) . '=' . urlencode_oauth ($value);
}
$q = implode ('&', $q);
// generate the base string for signature
$parts = array (
'POST',
urlencode_oauth ('https://api.linkedin.com/v1/people/~'),
urlencode_oauth ($q)
);
$base_string = implode ('&', $parts);
## step 4
$key = urlencode_oauth (LINKEDIN_SECRET) . '&' . urlencode_oauth ($_SESSION ['linkedin_access_token_secret']);
$signature = base64_encode (hash_hmac ('sha1', $base_string, $key, true));
## step 5
$params ['oauth_signature'] = $signature;
$str = array ();
foreach ($params as $key => $value)
{
$str [] = $key . '="' . urlencode_oauth ($value) . '"';
}
$str = implode(', ', $str);
$headers = array (
'POST /v1/people/~ HTTP/1.1',
'Host: api.linkedin.com',
'Authorization: OAuth ' . $str,
'Content-Type: text/xml;charset=UTF-8',
'Content-Length: 0',
'Connection: close'
);
## step 6
$fp = fsockopen ("ssl://api.linkedin.com", 443, $errno, $errstr, 30);
if (!$fp)
{
echo 'Unable to connect to LinkedIn';
exit();
}
$out = implode ("\r\n", $headers) . "\r\n\r\n";
fputs ($fp, $out);
// getting LinkedIn server response
$res = '';
while (!feof ($fp)) $res .= fgets ($fp, 4096);
fclose ($fp);
echo '<pre>';
echo $res . "\n\n";
echo $_SESSION ['linkedin_access_token'] . "\n" . $_SESSION ['linkedin_access_token_secret'];
?>
What's wrong with it? it shows me
HTTP/1.1 405 Method Not Allowed
Server: Apache-Coyote/1.1
x-li-request-id: H8M76QXW5O
Date: Tue, 04 Sep 2012 12:09:21 GMT
Vary: *
x-li-format: xml
Content-Type: text/xml;charset=UTF-8
Content-Length: 262
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<error>
<status>405</status>
<timestamp>1346760561727</timestamp>
<request-id>H8M76QXW5O</request-id>
<error-code>0</error-code>
<message>Unsupported POST target {/v1/people/~}</message>
</error>
xxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxx
define('LINKEDIN_KEY', 'YOUR_KEY');
define('LINKEDIN_SECRET', 'YOUR SECRET');
function urlencode_oauth($str) {
return str_replace('+',' ',str_replace('%7E','~',rawurlencode($str)));
}
$links = array(
'request_token'=>'https://api.linkedin.com/uas/oauth/requestToken',
'authorize'=>'https://www.linkedin.com/uas/oauth/authorize',
'access_token'=>'https://api.linkedin.com/uas/oauth/accessToken'
);
$params = array(
'oauth_callback'=>"YOUR CALLBACK URL",
'oauth_consumer_key'=>LINKEDIN_KEY,
'oauth_nonce'=>sha1(microtime()),
'oauth_signature_method'=>'HMAC-SHA1',
'oauth_timestamp'=>time(),
'oauth_version'=>'1.0'
);
// sort parameters according to ascending order of key
ksort($params);
// prepare URL-encoded query string
$q = array();
foreach ($params as $key=>$value) {
$q[] = urlencode_oauth($key).'='.urlencode_oauth($value);
}
$q = implode('&',$q);
// generate the base string for signature
$parts = array(
'POST',
urlencode_oauth($links['request_token']),
urlencode_oauth($q)
);
$base_string = implode('&',$parts);
$key = urlencode_oauth(LINKEDIN_SECRET) . '&';
$signature = base64_encode(hash_hmac('sha1',$base_string,$key,true));
$params['oauth_signature'] = $signature;
$str = array();
foreach ($params as $key=>$value) {
$str[] = $key . '="'.urlencode_oauth($value).'"';
}
$str = implode(', ',$str);
$headers = array(
'POST /uas/oauth/requestToken HTTP/1.1',
'Host: api.linkedin.com',
'Authorization: OAuth '.$str,
'Content-Type: text/xml;charset=UTF-8',
'Content-Length: 0',
'Connection: close'
);
$fp = fsockopen("ssl://api.linkedin.com",443,$errno,$errstr,30);
if (!$fp) { echo 'Unable to connect to LinkedIn'; exit(); }
$out = implode("\r\n",$headers) . "\r\n\r\n";
fputs($fp,$out);
// getting LinkedIn server response
$res = '';
while (!feof($fp)) $res .= fgets($fp,4096);
fclose($fp);
$parts = explode("\n\n",str_replace("\r",'',$res));
$res_headers = explode("\n",$parts[0]);
if ($res_headers[0] != 'HTTP/1.1 200 OK') {
echo 'Error getting OAuth token and secret.'; exit();
}
parse_str($parts[1],$data);
if (empty($data['oauth_token'])) {
echo 'Failed to get LinkedIn request token.'; exit();
}
Replace three things 1. 'YOUR_KEY' 2. 'YOUR_SECRET' and 3.'oauth_callback'.
Thanks,
Anand
As the documentation indicates, the Profile API does not support the POST method. Try using GET instead to retrieve profile data.
I have do post some information in cross domain . And i am achieving this thing by below code
<?php
function do_post_request($sendingurl, $data, $optional_headers = null) {
$params = array(
'http' => array(
'method' => 'POST',
'url' => $data
)
);
if ($optional_headers !== null) {
$params['http']['header'] = $optional_headers;
}
$ctx = stream_context_create($params);
$fp = #fopen($sendingurl, 'rb', false, $ctx);
if (!$fp) {
throw new Exception("Problem with $sendingurl, $php_errormsg");
}
$response = #stream_get_contents($fp);
if ($response === false) {
throw new Exception("Problem reading data from $sendingurl, $php_errormsg");
}
return $response;
}
$response = do_post_request('http://mag16.playtrickz.com/testing.php','http%3A%2F%2Fwww.facebook.com');
echo $response;
But it is not working .
On successful POST Request : It will display its value
otherwise it will show : nodata found.
Why it is not working and how to make them work .
Here is how I would write your function:
function do_post_request($url, $data = NULL, $optional_headers = NULL) {
// Build a body string from an array
$content = (is_array($data)) ? http_build_query($data) : '';
// Parse the array of headers and strip values we will be setting
$headers = array();
if (is_array($optional_headers)) {
foreach ($optional_headers as $name => $value) {
if (!in_array(strtolower($name), array('content-type', 'content-length', 'connection'))) {
$headers[$name] = $value;
}
}
}
// Add our pre-set headers
$headers['Content-Type'] = 'application/x-www-form-urlencoded';
$headers['Content-Length'] = strlen($content);
$headers['Connection'] = 'close';
// Build headers into a string
$header = array();
foreach ($headers as $name => $value) {
if (is_array($value)) {
foreach ($value as $multi) {
$header[] = "$name: $multi";
}
} else {
$header[] = "$name: $value";
}
}
$header = implode("\r\n", $header);
// Create the stream context
$params = array(
'http' => array(
'method' => 'POST',
'header' => $header,
'content' => $content
)
);
$ctx = stream_context_create($params);
// Make the request
$fp = #fopen($url, 'rb', FALSE, $ctx);
if (!$fp) {
throw new Exception("Problem with $url, $php_errormsg");
}
$response = #stream_get_contents($fp);
if ($response === FALSE) {
throw new Exception("Problem reading data from $url, $php_errormsg");
}
return $response;
}
This has been re-built so that the data to send to the server and the headers are passed in as associative arrays. So you would build an array that looks like you would want $_POST to look in the remote script, and pass that in. You can also pass an array of additional headers to send, but the function will automatically add a Content-Type, Content-Length and Connection header.
So your request would be called like this:
$data = array(
'url' => 'http://www.facebook.com/'
);
$response = do_post_request('http://mag16.playtrickz.com/testing.php', $data);
echo $response;