Files
DomFramework/httpclient.php

1055 lines
30 KiB
PHP

<?php
/** DomFramework
* @package domframework
* @author Dominique Fournier <dominique@fournier38.fr>
*/
/** This programe allow to get a HTTP page from a site, and examine the content.
* It will store the Cookies, allow to do the redirects, follow links and
* get form / input and send the values.
*/
class Httpclient
{
//////////////////////////
//// PROPERTIES ////
//////////////////////////
// {{{
/** The debug depth. 0: Nothing is displayed, 1: Only URL are displayed,
* 2: headers only, 3: all the content
*/
private $debug = null;
/** The URL to use
*/
private $url = "";
/** The cookies
*/
private $cookies = array ();
/** Store the session cookies when analyzing the answer of the server
*/
private $cookiesSession = true;
/** The headersReceived
*/
private $headersReceived = array ();
/** The headersSent to the server
*/
private $headersSent = array ();
/** The Method used to communicate : GET, POST, HEAD, PUT, DELETE
*/
private $method = "GET";
/** The TCP port used for connection
*/
private $port = null;
/** The TCPClient object
*/
private $tcpclient = null;
/** The SSL Options parameters (if set)
*/
private $ssloptions = array ();
/** The maximum maxsize allowed
*/
private $maxsize;
/** The returned HTTP code from the server
*/
private $httpCode = null;
/** The body size staying to read
*/
private $bodySize = null;
/** The content method to get the content : chunked or Content-Length
*/
private $contentMethod = false;
/** Follow X redirects before abort
*/
private $redirectMaxCount = 10;
/** The actual number of redirect
*/
private $redirectCount = 0;
/** The form data to send
* Will be of type array ("field" => "value")
* If value is like "@/tmp/file", use the /tmp/file as content
*/
private $formData = array ();
/** The raw data to send
*/
private $rawData = "";
/** The timeout in second before expiring the connection
*/
private $timeout = 30;
/** Store the user agent. If it is empty, it will not be sent to the server
*/
private $useragent = "HTTPClient";
/** The last valid page known. Used as referer
*/
private $referer = "";
/** The accept type of data wanted by the client
*/
private $accept = "text/html,application/xhtml+xml,application/xml;q=0.9,".
"*/*;q=0.8";
/** The accept language wanted by the client
*/
private $acceptLanguage = "en-us,en;q=0.5";
/** The accept encoding wanted by the client
*/
private $acceptEncoding = "gzip, deflate";
// }}}
/** The constructor
*/
public function __construct ()
// {{{
{
$maxsize = str_replace (array ('G', 'M', 'K'),
array ('000000000', '000000', '000'),
ini_get ('memory_limit'));
$maxsize = intval ($maxsize / 2);
// If no maxsize limit, set to 8G
if ($maxsize === 0)
$maxsize = 8000000000;
$this->maxsize = $maxsize;
$this->headersReset ();
}
// }}}
/////////////////////////////////
//// GETTERS / SETTERS ////
/////////////////////////////////
/** Set / Get the url
* @param string|null $url Set / Get the url
*/
public function url ($url = null)
// {{{
{
if ($url === null)
return $this->url;
if (! is_string ($url))
throw new \Exception ("Invalid url to set : not a string", 406);
$this->url = $url;
return $this;
}
// }}}
/** Set / Get the cookies stored
* @param array|null $cookies Set / Get the cookies
*/
public function cookies ($cookies = null)
// {{{
{
if ($cookies === null)
return $this->cookies;
if (! is_array ($cookies))
throw new \Exception ("Invalid cookies to set : not an array", 406);
$this->cookies = $cookies;
return $this;
}
// }}}
/** Set / Get the method
* @param string|null $method Set / Get the method
*/
public function method ($method = null)
// {{{
{
if ($method === null)
return $this->method;
if (! is_string ($method))
throw new \Exception ("Invalid method to set : not an string", 406);
$method = strtoupper ($method);
if (! in_array ($method, array ("GET", "POST", "PUT", "DELETE", "HEAD")))
throw new \Exception ("Invalid method to set : not in list", 406);
$this->method = $method;
return $this;
}
// }}}
/** Get the headersReceived after the page was get
*/
public function headersReceived ()
// {{{
{
return $this->headersReceived;
}
// }}}
/** Get the headers sent to the server after the page was get
*/
public function headersSent ()
// {{{
{
return $this->headersSent;
}
// }}}
/** Set the headers to initial value
*/
public function headersReset ()
// {{{
{
$this->headersSent = array ();
return $this;
}
// }}}
/** Add a new header to be sent to the server
* @param string $header The header to add/update
* @param string $value The value to save
*/
public function headerAdd ($header, $value)
// {{{
{
$this->headersSent[$header] = $value;
return $this;
}
// }}}
/** Get the port used for connection
*/
public function port ()
// {{{
{
return $this->port;
}
// }}}
/** Set / Get the maximum maxsize allowed
* @param integer|null $maxsize The maxsize in bytes
*/
public function maxsize ($maxsize = null)
// {{{
{
if ($maxsize === null)
return $this->maxsize;
$this->maxsize = intval ($maxsize);
return $this;
}
// }}}
/** Get the HTTP Return code from connection
*/
public function httpCode ()
// {{{
{
return $this->httpCode;
}
// }}}
/** Set / Get the debug mode
* 0: Nothing is displayed, 1: Only URL are displayed,
* 2: headers only send and received, 3: all the content, but without sent
* files, 4: all the content
* @param boolean|null $debug The debug value to set or get
*/
public function debug ($debug = null)
// {{{
{
if ($debug === null)
return $this->debug;
$this->debug = intval ($debug);
return $this;
}
// }}}
/** Set / Get the form Data
* Will be of type array ("field" => "value")
* If value is like "@/tmp/file", use the /tmp/file as content
* @param array|null $formData The data to send to the server
*/
public function formData ($formData = null)
// {{{
{
if ($formData === null)
return $this->formData;
if (! is_array ($formData))
throw new \Exception ("Invalid form data provided : not an array", 406);
$this->formData = $formData;
$this->rawData = "";
return $this;
}
// }}}
/** Set / Get the content Data
* The data is in raw format and will not be modified.
* Overwrite the eventually previous form data and rawData
* @param string|null $rawData The data to use
*/
public function rawData ($rawData = null)
// {{{
{
if ($rawData === null)
return $this->rawData;
if (! is_string ($rawData))
throw new \Exception ("Invalid raw data provided : not a string", 406);
$this->rawData = $rawData;
$this->formData = array ();
return $this;
}
// }}}
/** Get / Set the Store of session cookies when analyzing the answer of the
* server
* @param boolean|null $cookiesSession Allow to store the session cookies
*/
public function cookiesSession ($cookiesSession = null)
// {{{
{
if ($cookiesSession === null)
return $this->cookiesSession;
$this->cookiesSession = !! $cookiesSession;
return $this;
}
// }}}
/** Get / Set the maximum number of redirect to follow before aborting
* @param integer|null $redirectMaxCount The maximum number of redirect
* before exception
*/
public function redirectMaxCount ($redirectMaxCount = null)
// {{{
{
if ($redirectMaxCount === null)
return $this->redirectMaxCount;
$this->redirectMaxCount = intval ($redirectMaxCount);
return $this;
}
// }}}
/** Get / Set the actual number of redirect
* @param integer|null $redirectCount The actual number of redirect
*/
public function redirectCount ($redirectCount = null)
// {{{
{
if ($redirectCount === null)
return $this->redirectCount;
$this->redirectCount = intval ($redirectCount);
return $this;
}
// }}}
/** Get / Set the timeout in second before expiring the connection
* 30s by default
* @param integer|null $timeout The timeout value
*/
public function timeout ($timeout = null)
// {{{
{
if ($timeout === null)
return $this->timeout;
$this->timeout = intval ($timeout);
return $this;
}
// }}}
/** Get / Set the useragent sent to the server. If it is empty, it will not be
* sent
* @param string|null $useragent The user agent to use
*/
public function useragent ($useragent = null)
// {{{
{
if ($useragent === null)
return $this->useragent;
$this->useragent = $useragent;
return $this;
}
// }}}
/** Get/Set the referer page
* @param string|null $referer The new referer that will be used on next
* request
*/
public function referer ($referer = null)
// {{{
{
if ($referer === null)
return $this->referer;
$this->referer = $referer;
return $this;
}
// }}}
/** Get/Set the accept type of page wanted by the client
* @param string|null $accept The accept types with weight
*/
public function accept ($accept = null)
// {{{
{
if ($accept === null)
return $this->accept;
$this->accept = $accept;
return $this;
}
// }}}
/** Get/Set the accept Language wanted by the client
* @param string|null $acceptLanguage The languages with weight
*/
public function acceptLanguage ($acceptLanguage = null)
// {{{
{
if ($acceptLanguage === null)
return $this->acceptLanguage;
$this->acceptLanguage = $acceptLanguage;
return $this;
}
// }}}
/** Get/Set the accept Encoding wanted by the client
* @param string|null $acceptEncoding The encoding requested
*/
public function acceptEncoding ($acceptEncoding = null)
// {{{
{
if ($acceptEncoding === null)
return $this->acceptEncoding;
$this->acceptEncoding = $acceptEncoding;
return $this;
}
// }}}
/** Set the authentication in Basic type
* @param string $login The login to use
* @param string $password The password to use
*/
public function authBasic ($login, $password)
// {{{
{
$this->headersSent["Authorization"] = "Basic ".
base64_encode ("$login:$password");
return $this;
}
// }}}
//////////////////////////////////
//// THE ACTIVE METHODS ////
//////////////////////////////////
/** Get the page
* Will fill the headersReceived, cookies and port properties.
* This will fill all the RAM if the page is too big. For big files, use
* $httpclient->url ($url)
* ->connect () ;
* while ($content = $httpclient->read ()) {}
* $httpclient->disconnect ();
* If no maxsize limit is set, limit the download to 8G
* @param string $url The URL to get
* @param array|null $ssloptions The SSL options (stream_context_set_option)
* @return the page body
*/
public function getPage ($url, $ssloptions = array ())
// {{{
{
$this->method ("GET");
$this->url ($url);
$this->connect ($ssloptions);
return $this->getContent ();
}
// }}}
/** Init the connection to URL
* Will fill the headersReceived, cookies and port properties.
* @param array|null $ssloptions The SSL options (stream_context_set_option)
* @return $this
*/
public function connect ($ssloptions = array ())
// {{{
{
$this->log (2, "## URL Start $this->method $this->url");
$this->ssloptions = $ssloptions;
$this->headersReceived = array ();
$this->bodySize = null;
$this->httpCode = null;
if ($this->url === "")
throw new \Exception ("No URL set to connect", 406);
// Manage the URL (and the parameters in GET method)
// {{{
$parseURL = parse_url ($this->url);
if (! key_exists ("scheme", $parseURL))
throw new \Exception ("Scheme must be set to http or https", 406);
if ($parseURL["scheme"] !== "http" &&
$parseURL["scheme"] !== "https")
throw new \Exception ("Scheme must be http or https only", 406);
if (key_exists ("port", $parseURL))
$this->port = $parseURL["port"];
elseif (key_exists ("scheme", $parseURL))
{
if ($parseURL["scheme"] === "http")
$this->port = 80;
elseif ($parseURL["scheme"] === "https")
$this->port = 443;
}
if (! key_exists ("path", $parseURL))
$path = "/";
else
$path = $parseURL["path"];
if (key_exists ("query", $parseURL))
$path .= "?".$parseURL["query"];
if ($this->method === "GET" && ! empty ($this->formData))
{
// In GET method, the form data are added to the path
if (! key_exists ("query", $parseURL))
$path .= "?";
else
$path .= "&";
$i = 0;
foreach ($this->formData as $key => $val)
{
if ($i > 0)
$path .= "&";
$path .= rawurlencode ($key)."=";
if (isset ($val{0}) && $val{0} === "@")
{
$file = substr ($val, 1);
if (! file_exists ($file))
throw new \Exception ("Data file '$file' doesn't exists", 406);
$val = file_get_contents ($file);
}
$path .= rawurlencode ($val);
$i ++;
}
}
if (key_exists ("fragment", $parseURL))
$path .= "#".$parseURL["fragment"];
if (! key_exists ("host", $parseURL))
throw new \Exception ("No host provided to URL", 406);
// }}}
// Prepare the headers to be sent
// {{{
unset ($this->headersSent[0]);
array_unshift ($this->headersSent,
"$this->method $path HTTP/1.1");
$this->headersSent["Host"] = $parseURL["host"];
if ($this->acceptEncoding !== "")
$this->headersSent["Accept-Encoding"] = $this->acceptEncoding;
if ($this->acceptLanguage !== "")
$this->headersSent["Accept-Language"] = $this->acceptLanguage;
if ($this->accept != "")
$this->headersSent["Accept"] = $this->accept;
if ($this->useragent !== "")
$this->headersSent["User-Agent"] = $this->useragent;
if ($this->referer !== "")
$this->headersSent["Referer"] = $this->referer;
$this->headersSent["Connection"] = "keep-alive";
$cookies = $this->cookieToSend ($this->url);
if (! empty ($cookies))
{
$this->headersSent["Cookie"] = implode ("; ", $cookies);
}
if ($this->method !== "GET" && ! empty ($this->formData))
{
$this->headersSent["Content-Type"] = "application/x-www-form-urlencoded";
$len = 0;
foreach ($this->formData as $key => $val)
{
if ($len > 0)
$len++; // Add the &
$len += strlen (rawurlencode ($key)) + 1;
if (isset ($val{0}) && $val{0} === "@")
{
$file = substr ($val, 1);
if (! file_exists ($file))
throw new \Exception ("Data file '$file' doesn't exists", 406);
// TODO : Do a loop of 1MB for big files instead of loading the mem
$len += strlen (rawurlencode (file_get_contents ($file)));
}
else
$len += strlen (rawurlencode ($val));
}
$this->headersSent["Content-Length"] = $len;
}
if ($this->method !== "GET" && $this->rawData !== "")
{
if (! key_exists ("Content-Type", $this->headersSent))
$this->headersSent["Content-Type"] =
"application/x-www-form-urlencoded";
$this->headersSent["Content-Length"] = strlen ($this->rawData);
}
$this->log (2, "Headers Send :");
$this->log (2, $this->headersSent);
// }}}
// Send the request to the server
// {{{
if ($this->tcpclient === null)
{
$this->tcpclient = new tcpclient ($parseURL["host"], $this->port);
$this->tcpclient->timeout ($this->timeout);
$this->tcpclient->connect ();
if ($parseURL["scheme"] === "https")
$this->tcpclient->cryptoEnable (true, null, $this->ssloptions);
}
$this->tcpclient->readMode ("text");
$this->tcpclient->send ($this->headersSent[0]."\r\n");
foreach ($this->headersSent as $header => $value)
{
if ($header === 0)
continue;
$this->tcpclient->send ("$header: $value\r\n");
}
$this->tcpclient->send ("\r\n");
// }}}
// Send the POST data form if exists
// {{{
if ($this->method !== "GET" && ! empty ($this->formData))
{
$i = 0;
$this->log (3, "Body Send : \n");
foreach ($this->formData as $key => $val)
{
if ($i > 0)
{
$this->tcpclient->send ("&");
$this->log (3, "&");
}
$this->tcpclient->send (rawurlencode ($key)."=");
$this->log (3, rawurlencode ($key)."=");
if (isset ($val{0}) && $val{0} === "@")
{
$file = substr ($val, 1);
if (! file_exists ($file))
throw new \Exception ("Data file '$file' doesn't exists", 406);
// TODO : Do a loop of 1MB for big files instead of loading the mem
$val = file_get_contents ($file);
$this->tcpclient->send (rawurlencode ($val));
$this->log (4, rawurlencode ($val));
}
else
{
$this->tcpclient->send (rawurlencode ($val));
$this->log (3, rawurlencode ($val));
}
$i ++;
}
$this->log (3, "\n");
}
elseif ($this->method !== "GET" && $this->rawData !== "")
{
$this->tcpclient->send ($this->rawData);
$this->log (3, $this->rawData."\n");
}
// }}}
// Get the result header from the server
// {{{
$headers = array ();
while (($header = $this->tcpclient->read (4095)) !== "")
{
@list ($key, $val) = explode (":", $header, 2);
if ($val === null)
$headers[] = $header;
else
{
if (key_exists ($key, $headers))
{
if (! (is_array ($headers[$key])))
$headers[$key] = array ($headers[$key]);
$headers[$key][] = trim ($val);
}
else
$headers[$key] = trim ($val);
}
}
if (! key_exists (0, $headers))
throw new \Exception ("No HTTP code available from server", 500);
$this->headersReceived = $headers;
$returnCode = $headers[0];
preg_match_all ("#^HTTP/(?P<HTTPVersion>\d.\d) (?P<HTTPCode>\d+) ".
"(?P<HTTPString>.+)$#i", $returnCode, $matches);
if (isset ($matches["HTTPCode"][0]))
$this->httpCode = intval ($matches["HTTPCode"][0]);
// Add the received cookies to property
if (isset ($headers["Set-Cookie"]))
{
if (! is_array ($headers["Set-Cookie"]))
$cookies = array ($headers["Set-Cookie"]);
else
$cookies = $headers["Set-Cookie"];
foreach ($cookies as $cookie)
{
// The invalid cookies are silently dropped
$this->cookieAdd ($parseURL["host"], $cookie);
}
}
$this->log (2, "Headers Received :");
$this->log (2, $this->headersReceived);
$this->contentMethod = false;
if (key_exists ("Transfer-Encoding", $headers) &&
$headers["Transfer-Encoding"] === "chunked")
{
$this->contentMethod = "chunked";
$this->bodySize = 0;
}
elseif (key_exists ("Content-Length", $headers))
{
$this->contentMethod = "Content-Length";
$this->bodySize = $headers["Content-Length"];
}
elseif (key_exists ("Connection", $headers) &&
$headers["Connection"] === "close")
{
// Connection closed by server. Nothing to get
}
elseif ($this->httpCode !== 204 && $this->httpCode !== 301 &&
$this->httpCode !== 302)
{
throw new \Exception ("No transfert content provided", 500);
}
if ($this->contentMethod === "chunked")
$this->log (1, "URL $this->method $this->url $this->httpCode Chunked");
elseif (is_numeric ($this->bodySize))
$this->log (1, "URL $this->method $this->url $this->httpCode ".
$this->bodySize);
else
$this->log (1, "URL $this->method $this->url $this->httpCode 0");
// }}}
return $this;
}
// }}}
/** Get the content from the server and put it in memory
*/
public function getContent ()
// {{{
{
$url = $this->url;
$content = "";
while ($tmp = $this->read (1000000))
{
$content .= $tmp;
if (strlen ($content) > $this->maxsize)
throw new \Exception ("File to get exceeded maxsize", 500);
}
if (key_exists ("Content-Encoding", $this->headersReceived))
{
if ($this->headersReceived["Content-Encoding"] === "gzip" &&
function_exists ("gzdecode"))
$content = gzdecode ($content);
}
$this->disconnect ();
$this->headersReset ();
$this->formData = "";
$this->rawData = "";
if ($this->httpCode === 301 || $this->httpCode === 302 ||
key_exists ("Location", $this->headersReceived))
{
if (! key_exists ("Location", $this->headersReceived))
throw new \Exception ("Redirect without location provided", 406);
$this->redirectCount++;
if ($this->redirectCount > $this->redirectMaxCount)
throw new \Exception ("Redirect exceed maximum limit", 406);
// echo "REDIRECT TO ".$this->headersReceived["Location"]."\n";
$location = $this->headersReceived["Location"];
$parseURLInit = parse_url ($url);
$parseURLLocation = parse_url ($location);
if ($parseURLLocation["path"]{0} !== "/" &&
key_exists ("path", $parseURLInit))
$location = dirname ($parseURLInit["path"]).$location;
if (! key_exists ("port", $parseURLLocation) &&
key_exists ("port", $parseURLInit))
$location = ":".$parseURLInit["port"].$location;
if (! key_exists ("host", $parseURLLocation))
$location = $parseURLInit["host"].$location;
if (! key_exists ("scheme", $parseURLLocation))
$location = $parseURLInit["scheme"]."://".$location;
$this->log (1, "REDIRECT $this->httpCode to $location");
$this->log (2, " content=$content\n");
$content = $this->getPage ($location, $this->ssloptions);
}
$this->referer = $url;
$this->redirectCount = 0;
return $content;
}
// }}}
/** Read max MAXSIZE bytes
* Return "" if all the file is received
* 3 methods are supported : Chunked mode, Content-Length defined and all
* until the connection will be closed by the server
* @param integer $maxsize The maxsize to get in this read
*/
public function read ($maxsize = 4096)
// {{{
{
if ($this->tcpclient === null)
throw new \Exception ("HTTPClient : can not read non connected URL", 406);
$this->tcpclient->timeout ($this->timeout);
$content = "";
if ($this->contentMethod === "chunked" && $this->bodySize === 0)
{
// Get the body chunk size
$this->tcpclient->readMode ("text");
$size = trim ($this->tcpclient->read ());
$this->bodySize = hexdec ($size);
}
if ($this->bodySize === 0)
return "";
if ($this->bodySize === null)
$toBeRead = $maxsize;
else
$toBeRead = $this->bodySize;
if ($toBeRead > $maxsize)
$toBeRead = $maxsize;
$this->tcpclient->readMode ("binary");
if ($toBeRead > 0)
$content = $this->tcpclient->read ($toBeRead);
// In close mode, the bodySize is not set and should not be updated
if ($this->bodySize !== null)
$this->bodySize = $this->bodySize - strlen ($content);
if ($this->contentMethod === "chunked" && $this->bodySize === 0)
{
// Get the Carriage return before the next chunk size
$this->tcpclient->readMode ("text");
$cr = trim ($this->tcpclient->read ());
}
$this->log (3, $content);
return $content;
}
// }}}
/** Disconnect the connection
*/
public function disconnect ()
// {{{
{
$this->tcpclient = null;
}
// }}}
/** Display the log message
* @param integer $priority The minimal priority to display the message
* @param mixed $message The message to display
*/
public function log ($priority, $message)
{
if ($this->debug < $priority)
return;
if (is_array ($message))
print_r ($message);
elseif (is_bool ($message))
{
if ($message) echo "TRUE\n"; else echo "FALSE\n";
}
else
{
echo "$message";
if ($priority < 3)
echo "\n";
}
}
/** Return the base URL of the site
* @return the URL
*/
public function baseURL ()
// {{{
{
if ($this->url === "")
throw new \Exception ("Can not get baseURL of empty url", 500);
$parseURL = parse_url ($this->url);
$scheme = isset ($parseURL['scheme']) ? $parseURL['scheme'] . '://' : '';
$host = isset ($parseURL['host']) ? $parseURL['host'] : '';
$port = isset ($parseURL['port']) ? ':' . $parseURL['port'] : '';
$user = isset ($parseURL['user']) ? $parseURL['user'] : '';
$pass = isset ($parseURL['pass']) ? ':' . $parseURL['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
return "$scheme$user$pass$host$port";
}
// }}}
//////////////////////////////////
//// COOKIES MANAGEMENT ////
//////////////////////////////////
/** The cookies are stored in Netscape cookies.txt file :
The layout of Netscape's cookies.txt file is such that each line contains
one name-value pair. An example cookies.txt file may have an entry that
looks like this:
.netscape.com TRUE / FALSE 946684799 NETSCAPE_ID 100103
Each line represents a single piece of stored information. A tab is inserted
between each of the fields.
From left-to-right, here is what each field represents:
domain : The domain that created AND that can read the variable.
flag : A TRUE/FALSE value indicating if all machines within a given
domain can access the variable. This value is set automatically
by the browser, depending on the value you set for domain.
path : The path within the domain that the variable is valid for.
secure : A TRUE/FALSE value indicating if a secure connection with the
domain is needed to access the variable.
expiration : The UNIX time that the variable will expire on. UNIX time is
defined as the number of seconds since Jan 1, 1970 00:00:00
GMT.
name : The name of the variable.
value : The value of the variable.
*/
/** Add a cookie in the store
* If the cookie already exists, the old one is replaced by the new value
* @param string $domain The domain to use
* @param string $cookie The cookie content to store
*/
public function cookieAdd ($domain, $cookie)
// {{{
{
// echo "COOKIE = $cookie\n";
$content = explode (";", $cookie);
$flag = "FALSE";
$path = "/";
$secure = "FALSE";
$expiration = 0;
$name = "";
$value = "";
foreach ($content as $part)
{
@list ($key, $val) = explode ("=", $part, 2);
$key = trim ($key);
if (strtolower ($key) === "path") $path = $val;
elseif (strtolower ($key) === "domain")
{
// Check if $domain is compatible with $key before storing the cookie
if (substr ($domain, -1 * strlen ($val)) === $val)
{
$domain = $val;
$flag = "TRUE";
}
else
{
return "Invalid domain provided";
}
}
elseif (strtolower ($key) === "expires")
{
try
{
$date = new \DateTime ($val);
$expiration = $date->getTimestamp();
}
catch (\Exception $e)
{
return "Invalid expires date provided";
}
}
elseif ($val !== null && $name === "")
{
// Only the first value will be stored as cookie (name, val) pair
// echo "KEY=$key => $val\n";
$name = $key;
$value = $val;
}
elseif ($val !== null)
{
// echo "Not managed key=>val $key=>$val\n";
}
else
{
// No value provided : no test
}
}
$cookieLine = "$domain\t$flag\t$path\t$secure\t$expiration\t$name\t$value";
if (strlen ($cookieLine) > 4096)
return "Cookie value too long";
if ($expiration === 0 && $this->cookiesSession === false)
{
// echo "Do not store Session cookies\n";
return;
}
$found = false;
foreach ($this->cookies as $key => $storedCookie)
{
$storedCookie = explode ("\t", $storedCookie);
if (! key_exists (0, $storedCookie) ||
! key_exists (5, $storedCookie))
continue;
if ($storedCookie[0] !== $domain || $storedCookie[5] !== $name)
continue;
if ($expiration > 0 && $expiration < time ())
{
//echo "Remove the already set cookie for $domain $name : expired\n";
unset ($this->cookies[$key]);
$found = true;
}
else
{
//echo "Update the already set cookie for $domain $name\n";
$this->cookies[$key] = $cookieLine;
$found = true;
}
}
if ($found === false)
{
//echo "Append the new cookie for $domain $name\n";
$this->cookies[] = $cookieLine;
}
}
// }}}
/** Check if some stored cookies must be send to the server, because they are
* in the same domain.
* @param string $url The URL requested
* @return array the cookies to add to the headers send to the server
*/
public function cookieToSend ($url)
// {{{
{
$parseURL = parse_url ($this->url);
if ($parseURL === false)
return array ();
if (! key_exists ("host", $parseURL))
return array ();
if (! key_exists ("path", $parseURL))
$parseURL["path"] = "/";
if ($parseURL["path"]{0} !== "/")
$parseURL["path"] = "/".$parseURL["path"];
$res = array ();
foreach ($this->cookies as $storedCookie)
{
$storedCookie = explode ("\t", $storedCookie);
if (! key_exists (0, $storedCookie) ||
! key_exists (2, $storedCookie) ||
! key_exists (5, $storedCookie) ||
! key_exists (6, $storedCookie))
continue;
if ($storedCookie[0] !== substr ($parseURL["host"],
-1 * strlen ($storedCookie[0])) ||
$storedCookie[2] !== substr ($parseURL["path"], 0,
strlen ($storedCookie[2])))
continue;
$res[] = $storedCookie[5]."=".$storedCookie[6];
}
return $res;
}
// }}}
}