*/ class LinkbackHTTPClient extends \dokuwiki\HTTP\DokuHTTPClient { var $max_bodysize_limit = false; function __construct() { parent::__construct(); } /** * Simple function to do a GET request * * Returns the wanted page or false on an error; * * @param string $url The URL to fetch * @param bool $sloppy304 Return body on 304 not modified * @author Andreas Gohr */ function get($url,$sloppy304=false){ if(!$this->sendRequest($url)) return false; if($this->status == 304 && $sloppy304) return $this->resp_body; if($this->status < 200 || $this->status > 206) return false; return $this->resp_body; } /** * Simple function to do a POST request * * Returns the resulting page or false on an error; * * @author Andreas Gohr */ function post($url,$data){ if(!$this->sendRequest($url,$data,'POST')) return false; if($this->status < 200 || $this->status > 206) return false; return $this->resp_body; } /** * Do an HTTP request * * @author Andreas Goetz * @author Andreas Gohr */ function sendRequest($url, $data = array (), $method = 'GET') { $this->error = ''; $this->status = 0; // parse URL into bits $uri = parse_url($url); $server = $uri['host']; $path = $uri['path']; if (empty ($path)) $path = '/'; if (!empty ($uri['query'])) $path .= '?' . $uri['query']; $port = $uri['port']; if ($uri['user']) $this->user = $uri['user']; if ($uri['pass']) $this->pass = $uri['pass']; // proxy setup if ($this->proxy_host) { $request_url = $url; $server = $this->proxy_host; $port = $this->proxy_port; if (empty ($port)) $port = 8080; } else { $request_url = $path; if (empty ($port)) $port = ($uri['scheme'] == 'https') ? 443 : 80; } // add SSL stream prefix if needed - needs SSL support in PHP if ($port == 443 || $this->proxy_ssl) $server = 'ssl://' . $server; // prepare headers $headers = $this->headers; $headers['Host'] = $uri['host']; $headers['User-Agent'] = $this->agent; $headers['Referer'] = $this->referer; $headers['Connection'] = 'Close'; $post = ''; if ($method == 'POST') { $post = $this->postEncode($data); $headers['Content-Type'] = 'application/x-www-form-urlencoded'; $headers['Content-Length'] = strlen($post); } if ($this->user) { $headers['Authorization'] = 'BASIC ' . base64_encode($this->user . ':' . $this->pass); } if ($this->proxy_user) { $headers['Proxy-Authorization'] = 'BASIC ' . base64_encode($this->proxy_user . ':' . $this->proxy_pass); } // stop time $start = time(); // open socket $socket = @ fsockopen($server, $port, $errno, $errstr, $this->timeout); if (!$socket) { $this->status = '-100'; $this->error = "Could not connect to $server:$port\n$errstr ($errno)"; return false; } //set non blocking stream_set_blocking($socket, 0); // build request $request = "$method $request_url HTTP/" . $this->http . HTTP_NL; $request .= $this->buildHeaders($headers); $request .= $this->getCookies(); $request .= HTTP_NL; $request .= $post; $this->debug('request', $request); // send request fputs($socket, $request); // read headers from socket $r_headers = ''; do { if (time() - $start > $this->timeout) { $this->status = -100; $this->error = 'Timeout while reading headers'; return false; } if (feof($socket)) { $this->error = 'Premature End of File (socket)'; return false; } $r_headers .= fread($socket, 1); #FIXME read full lines here? } while (!preg_match('/\r\n\r\n$/', $r_headers)); $this->debug('response headers', $r_headers); // check if expected body size exceeds allowance if ($this->max_bodysize && preg_match('/\r\nContent-Length:\s*(\d+)\r\n/i', $r_headers, $match)) { if ($match[1] > $this->max_bodysize) { $this->error = 'Reported content length exceeds allowed response size'; if (!$this->max_bodysize_limit) { return false; } } } // get Status if (!preg_match('/^HTTP\/(\d\.\d)\s*(\d+).*?\n/', $r_headers, $m)) { $this->error = 'Server returned bad answer'; return false; } $this->status = $m[2]; // handle headers and cookies $this->resp_headers = $this->parseHeaders($r_headers); if (isset ($this->resp_headers['set-cookie'])) { foreach ((array) $this->resp_headers['set-cookie'] as $cookie) { list ($key, $value, $foo) = explode('=', $cookie); $this->cookies[$key] = $value; } } $this->debug('Object headers', $this->resp_headers); // check server status code to follow redirect if ($this->status == 301 || $this->status == 302) { if (empty ($this->resp_headers['location'])) { $this->error = 'Redirect but no Location Header found'; return false; } elseif ($this->redirect_count == $this->max_redirect) { $this->error = 'Maximum number of redirects exceeded'; return false; } else { $this->redirect_count++; $this->referer = $url; if (!preg_match('/^http/i', $this->resp_headers['location'])) { $this->resp_headers['location'] = $uri['scheme'] . '://' . $uri['host'] . $this->resp_headers['location']; } // perform redirected request, always via GET (required by RFC) return $this->sendRequest($this->resp_headers['location'], array (), 'GET'); } } // check if headers are as expected if ($this->header_regexp && !preg_match($this->header_regexp, $r_headers)) { $this->error = 'The received headers did not match the given regexp'; return false; } //read body (with chunked encoding if needed) $r_body = ''; if (preg_match('/transfer\-(en)?coding:\s*chunked\r\n/i', $r_headers)) { do { unset ($chunk_size); do { if (feof($socket)) { $this->error = 'Premature End of File (socket)'; return false; } if (time() - $start > $this->timeout) { $this->status = -100; $this->error = 'Timeout while reading chunk'; return false; } $byte = fread($socket, 1); $chunk_size .= $byte; } while (preg_match('/[a-zA-Z0-9]/', $byte)); // read chunksize including \r $byte = fread($socket, 1); // readtrailing \n $chunk_size = hexdec($chunk_size); $this_chunk = fread($socket, $chunk_size); $r_body .= $this_chunk; if ($chunk_size) { $byte = fread($socket, 2); // read trailing \r\n } if ($this->max_bodysize && strlen($r_body) > $this->max_bodysize) { $this->error = 'Allowed response size exceeded'; if ($this->max_bodysize_limit) { break; } else { return false; } } } while ($chunk_size); } else { // read entire socket while (!feof($socket)) { if (time() - $start > $this->timeout) { $this->status = -100; $this->error = 'Timeout while reading response'; return false; } $r_body .= fread($socket, 4096); if ($this->max_bodysize && strlen($r_body) > $this->max_bodysize) { $this->error = 'Allowed response size exceeded'; if ($this->max_bodysize_limit) { break; } else { return false; } } } } // close socket $status = socket_get_status($socket); fclose($socket); // decode gzip if needed if ($this->resp_headers['content-encoding'] == 'gzip') { $this->resp_body = gzinflate(substr($r_body, 10)); } else { $this->resp_body = $r_body; } $this->debug('response body', $this->resp_body); $this->redirect_count = 0; return true; } }