]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/HttpClient.php
fix some warnings
[SourceForge/phpwiki.git] / lib / HttpClient.php
1 <?php // -*-php-*-
2 rcs_id('$Id: HttpClient.php,v 1.3 2004-04-12 17:37:32 rurban Exp $');
3
4 /** 
5    Version 0.9, 6th April 2003 - Simon Willison ( http://simon.incutio.com/ )
6    Manual: http://scripts.incutio.com/httpclient/
7
8    Copyright © 2003 Incutio Limited
9    License: http://www.opensource.org/licenses/artistic-license.php
10 */
11
12 class HttpClient {
13     // Request vars
14     var $host;
15     var $port;
16     var $path;
17     var $method;
18     var $postdata = '';
19     var $cookies = array();
20     var $referer;
21     var $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*';
22     var $accept_encoding = 'gzip';
23     var $accept_language = 'en-us';
24     var $user_agent = 'Incutio HttpClient v0.9';
25     // Options
26     var $timeout = 20;
27     var $use_gzip = true;
28     var $persist_cookies = true;  // If true, received cookies are placed in the $this->cookies array ready for the next request
29                                   // Note: This currently ignores the cookie path (and time) completely. Time is not important, 
30                                   //       but path could possibly lead to security problems.
31     var $persist_referers = true; // For each request, sends path of last request as referer
32     var $debug = false;
33     var $handle_redirects = true; // Auaomtically redirect if Location or URI header is found
34     var $max_redirects = 5;
35     var $headers_only = false;    // If true, stops receiving once headers have been read.
36     // Basic authorization variables
37     var $username;
38     var $password;
39     // Response vars
40     var $status;
41     var $headers = array();
42     var $content = '';
43     var $errormsg;
44     // Tracker variables
45     var $redirect_count = 0;
46     var $cookie_host = '';
47
48     function HttpClient($host, $port=80) {
49         $this->host = $host;
50         $this->port = $port;
51     }
52     function get($path, $data = false) {
53         $this->path = $path;
54         $this->method = 'GET';
55         if ($data) {
56             $this->path .= '?'.$this->buildQueryString($data);
57         }
58         return $this->doRequest();
59     }
60     function post($path, $data) {
61         $this->path = $path;
62         $this->method = 'POST';
63         $this->postdata = $this->buildQueryString($data);
64         return $this->doRequest();
65     }
66     function buildQueryString($data) {
67         $querystring = '';
68         if (is_array($data)) {
69             // Change data in to postable data
70                 foreach ($data as $key => $val) {
71                         if (is_array($val)) {
72                                 foreach ($val as $val2) {
73                                         $querystring .= urlencode($key).'='.urlencode($val2).'&';
74                                 }
75                         } else {
76                                 $querystring .= urlencode($key).'='.urlencode($val).'&';
77                         }
78                 }
79                 $querystring = substr($querystring, 0, -1); // Eliminate unnecessary &
80         } else {
81             $querystring = $data;
82         }
83         return $querystring;
84     }
85     function doRequest() {
86         // Performs the actual HTTP request, returning true or false depending on outcome
87                 if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) {
88                     // Set error message
89             switch($errno) {
90                                 case -3:
91                                         $this->errormsg = 'Socket creation failed (-3)';
92                                 case -4:
93                                         $this->errormsg = 'DNS lookup failure (-4)';
94                                 case -5:
95                                         $this->errormsg = 'Connection refused or timed out (-5)';
96                                 default:
97                                         $this->errormsg = 'Connection failed ('.$errno.')';
98                             $this->errormsg .= ' '.$errstr;
99                             $this->debug($this->errormsg);
100                         }
101                         return false;
102         }
103         socket_set_timeout($fp, $this->timeout);
104         $request = $this->buildRequest();
105         $this->debug('Request', $request);
106         fwrite($fp, $request);
107         // Reset all the variables that should not persist between requests
108         $this->headers = array();
109         $this->content = '';
110         $this->errormsg = '';
111         // Set a couple of flags
112         $inHeaders = true;
113         $atStart = true;
114         // Now start reading back the response
115         while (!feof($fp)) {
116             $line = fgets($fp, 4096);
117             if ($atStart) {
118                 // Deal with first line of returned data
119                 $atStart = false;
120                 if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) {
121                     $this->errormsg = "Status code line invalid: ".htmlentities($line);
122                     $this->debug($this->errormsg);
123                     return false;
124                 }
125                 $http_version = $m[1]; // not used
126                 $this->status = $m[2];
127                 $status_string = $m[3]; // not used
128                 $this->debug(trim($line));
129                 continue;
130             }
131             if ($inHeaders) {
132                 if (trim($line) == '') {
133                     $inHeaders = false;
134                     $this->debug('Received Headers', $this->headers);
135                     if ($this->headers_only) {
136                         break; // Skip the rest of the input
137                     }
138                     continue;
139                 }
140                 if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) {
141                     // Skip to the next header
142                     continue;
143                 }
144                 $key = strtolower(trim($m[1]));
145                 $val = trim($m[2]);
146                 // Deal with the possibility of multiple headers of same name
147                 if (isset($this->headers[$key])) {
148                     if (is_array($this->headers[$key])) {
149                         $this->headers[$key][] = $val;
150                     } else {
151                         $this->headers[$key] = array($this->headers[$key], $val);
152                     }
153                 } else {
154                     $this->headers[$key] = $val;
155                 }
156                 continue;
157             }
158             // We're not in the headers, so append the line to the contents
159             $this->content .= $line;
160         }
161         fclose($fp);
162         // If data is compressed, uncompress it
163         if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') {
164             $this->debug('Content is gzip encoded, unzipping it');
165             $this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php
166             $this->content = gzinflate($this->content);
167         }
168         // If $persist_cookies, deal with any cookies
169         if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host) {
170             $cookies = $this->headers['set-cookie'];
171             if (!is_array($cookies)) {
172                 $cookies = array($cookies);
173             }
174             foreach ($cookies as $cookie) {
175                 if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) {
176                     $this->cookies[$m[1]] = $m[2];
177                 }
178             }
179             // Record domain of cookies for security reasons
180             $this->cookie_host = $this->host;
181         }
182         // If $persist_referers, set the referer ready for the next request
183         if (isset($this->persist_referers)) {
184             $this->debug('Persisting referer: '.$this->getRequestURL());
185             $this->referer = $this->getRequestURL();
186         }
187         // Finally, if handle_redirects and a redirect is sent, do that
188         if (isset($this->handle_redirects)) {
189             if (++$this->redirect_count >= $this->max_redirects) {
190                 $this->errormsg = 'Number of redirects exceeded maximum ('.$this->max_redirects.')';
191                 $this->debug($this->errormsg);
192                 $this->redirect_count = 0;
193                 return false;
194             }
195             $location = isset($this->headers['location']) ? $this->headers['location'] : '';
196             $uri = isset($this->headers['uri']) ? $this->headers['uri'] : '';
197             if ($location || $uri) {
198                 $url = parse_url($location.$uri);
199                 // This will FAIL if redirect is to a different site
200                 return $this->get($url['path']);
201             }
202         }
203         return true;
204     }
205     function buildRequest() {
206         $headers = array();
207         $headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding
208         $headers[] = "Host: {$this->host}";
209         $headers[] = "User-Agent: {$this->user_agent}";
210         $headers[] = "Accept: {$this->accept}";
211         if ($this->use_gzip) {
212             $headers[] = "Accept-encoding: {$this->accept_encoding}";
213         }
214         $headers[] = "Accept-language: {$this->accept_language}";
215         if (!empty($this->referer)) {
216             $headers[] = "Referer: {$this->referer}";
217         }
218         // Cookies
219         if (!empty($this->cookies)) {
220             $cookie = 'Cookie: ';
221             foreach ($this->cookies as $key => $value) {
222                 $cookie .= "$key=$value; ";
223             }
224             $headers[] = $cookie;
225         }
226         // Basic authentication
227         if (!empty($this->username) && !empty($this->password)) {
228             $headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password);
229         }
230         // If this is a POST, set the content type and length
231         if ($this->postdata) {
232             $headers[] = 'Content-Type: application/x-www-form-urlencoded';
233             $headers[] = 'Content-Length: '.strlen($this->postdata);
234         }
235         $request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata;
236         return $request;
237     }
238     function getStatus() {
239         return $this->status;
240     }
241     function getContent() {
242         return $this->content;
243     }
244     function getHeaders() {
245         return $this->headers;
246     }
247     function getHeader($header) {
248         $header = strtolower($header);
249         if (isset($this->headers[$header])) {
250             return $this->headers[$header];
251         } else {
252             return false;
253         }
254     }
255     function getError() {
256         return $this->errormsg;
257     }
258     function getCookies() {
259         return $this->cookies;
260     }
261     function getRequestURL() {
262         $url = 'http://'.$this->host;
263         if ($this->port != 80) {
264             $url .= ':'.$this->port;
265         }            
266         $url .= $this->path;
267         return $url;
268     }
269     // Setter methods
270     function setUserAgent($string) {
271         $this->user_agent = $string;
272     }
273     function setAuthorization($username, $password) {
274         $this->username = $username;
275         $this->password = $password;
276     }
277     function setCookies($array) {
278         $this->cookies = $array;
279     }
280     // Option setting methods
281     function useGzip($boolean) {
282         $this->use_gzip = $boolean;
283     }
284     function setPersistCookies($boolean) {
285         $this->persist_cookies = $boolean;
286     }
287     function setPersistReferers($boolean) {
288         $this->persist_referers = $boolean;
289     }
290     function setHandleRedirects($boolean) {
291         $this->handle_redirects = $boolean;
292     }
293     function setMaxRedirects($num) {
294         $this->max_redirects = $num;
295     }
296     function setHeadersOnly($boolean) {
297         $this->headers_only = $boolean;
298     }
299     function setDebug($boolean) {
300         $this->debug = $boolean;
301     }
302     // "Quick" static methods
303     function quickGet($url) {
304         $bits = parse_url($url);
305         $host = $bits['host'];
306         $port = isset($bits['port']) ? $bits['port'] : 80;
307         $path = isset($bits['path']) ? $bits['path'] : '/';
308         if (isset($bits['query'])) {
309             $path .= '?'.$bits['query'];
310         }
311         $client = new HttpClient($host, $port);
312         if (!$client->get($path)) {
313             return false;
314         } else {
315             return $client->getContent();
316         }
317     }
318     function quickPost($url, $data) {
319         $bits = parse_url($url);
320         $host = $bits['host'];
321         $port = isset($bits['port']) ? $bits['port'] : 80;
322         $path = isset($bits['path']) ? $bits['path'] : '/';
323         $client = new HttpClient($host, $port);
324         if (!$client->post($path, $data)) {
325             return false;
326         } else {
327             return $client->getContent();
328         }
329     }
330     function debug($msg, $object = false) {
331         if ($this->debug) {
332             print '<div style="border: 1px solid red; padding: 0.5em; margin: 0.5em;"><strong>HttpClient Debug:</strong> '.$msg;
333             if ($object) {
334                 ob_start();
335                     print_r($object);
336                     $content = htmlentities(ob_get_contents());
337                     ob_end_clean();
338                     print '<pre>'.$content.'</pre>';
339                 }
340                 print '</div>';
341         }
342     }   
343 }
344
345 ?>