3 /*************************************************
5 Snoopy - the PHP net client
6 Author: Monte Ohrt <monte@ispi.net>
7 Copyright (c): 1999-2000 ispi, all rights reserved
8 Version: 1.0 (plus - see SJM comments below)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 You may contact the author of Snoopy by e-mail at:
33 The latest version of Snoopy can be obtained from:
34 http://snoopy.sourceforge.com
38 SJM - alpha-grade changes based on the version of Snoopy released with MagpieRSS 0.7
40 comments to steve@minutillo.com
44 1) If this is PHP 4.3 or greater, and 'openssl' is available,
45 use the PHP built in SSL support for "https" instead of calling curl externally.
46 Use of external curl can still be forced by setting $use_curl = true.
48 ref: http://us2.php.net/fsockopen
50 2) HTTP Digest Authentication. If you set a username and password, basic auth
51 will be tried first. If that fails, and the server sends back an
52 WWW-Authenticate: Digest header, the request will be retried with the appropriate
53 digest response. Only qop=auth is supported, with MD5 as the algorithm.
54 I realize that sending basic auth first, and then following up with a digest
55 challenge-response kind of defeats the purpose in terms of security.
57 ref: http://www.faqs.org/rfcs/rfc2617.html
59 *************************************************/
63 /**** Public variables ****/
65 /* user definable vars */
67 var $host = "www.php.net"; // host name we are connecting to
68 var $port = 80; // port we are connecting to
69 var $proxy_host = ""; // proxy host to use
70 var $proxy_port = ""; // proxy port to use
71 var $agent = "Snoopy v1.0"; // agent we masquerade as
72 var $referer = ""; // referer info to pass
73 var $cookies = array(); // array of cookies to pass
74 // $cookies["username"]="joe";
75 var $rawheaders = array(); // array of raw headers to send
76 // $rawheaders["Content-type"]="text/html";
78 var $maxredirs = 5; // http redirection depth maximum. 0 = disallow
79 var $lastredirectaddr = ""; // contains address of last redirected address
80 var $offsiteok = true; // allows redirection off-site
81 var $maxframes = 0; // frame content depth maximum. 0 = disallow
82 var $expandlinks = true; // expand links to fully qualified URLs.
83 // this only applies to fetchlinks()
85 var $passcookies = true; // pass set cookies back through redirects
86 // NOTE: this currently does not respect
87 // dates, domains or paths.
89 var $user = ""; // user for http authentication
90 var $pass = ""; // password for http authentication
93 var $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*";
95 var $results = ""; // where the content is put
97 var $error = ""; // error messages sent here
98 var $response_code = ""; // response code returned from server
99 var $headers = array(); // headers returned from server sent here
100 var $maxlength = 500000; // max return data length (body)
101 var $read_timeout = 0; // timeout on read operations, in seconds
102 // supported only since PHP 4 Beta 4
103 // set to 0 to disallow timeouts
104 var $timed_out = false; // if a read operation timed out
105 var $status = 0; // http request status
107 var $curl_path = "/usr/bin/curl";
108 // Snoopy will use cURL for fetching
109 // SSL content if a full system path to
110 // the cURL binary is supplied here.
111 // set to false if you do not have
112 // cURL installed. See http://curl.haxx.se
113 // for details on installing cURL.
114 // Snoopy does *not* use the cURL
115 // library functions built into php,
116 // as these functions are not stable
117 // as of this Snoopy release.
119 // SJM - always use curl for HTTPS requests?
120 var $use_curl = false;
123 // send Accept-encoding: gzip?
124 var $use_gzip = true;
126 /**** Private variables ****/
128 var $_maxlinelen = 4096; // max line length (headers)
130 var $_scheme = "http"; // default scheme
131 var $_httpmethod = "GET"; // default http request method
132 var $_httpversion = "HTTP/1.0"; // default http request version
133 var $_submit_method = "POST"; // default submit method
134 var $_submit_type = "application/x-www-form-urlencoded"; // default submit type
135 var $_mime_boundary = ""; // MIME boundary for multipart/form-data submit type
136 var $_redirectaddr = false; // will be set if page fetched is a redirect
137 var $_redirectdepth = 0; // increments on an http redirect
138 var $_trieddigest = false; // have we tried Digest auth yet?
139 var $_frameurls = array(); // frame src urls
140 var $_framedepth = 0; // increments on frame depth
142 var $_isproxy = false; // set if using a proxy server
143 var $_fp_timeout = 30; // timeout for socket connection
145 /*======================================================================*\
147 Purpose: fetch the contents of a web page
148 (and possibly other protocols in the
149 future like ftp, nntp, gopher, etc.)
150 Input: $URI the location of the page to fetch
151 Output: $this->results the output text from the fetch
152 \*======================================================================*/
157 //preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS);
158 $URI_PARTS = parse_url($URI);
159 if (!empty($URI_PARTS["user"]))
160 $this->user = urldecode($URI_PARTS["user"]);
161 if (!empty($URI_PARTS["pass"]))
162 $this->pass = urldecode($URI_PARTS["pass"]);
164 $this->_scheme = $URI_PARTS["scheme"];
166 switch($URI_PARTS["scheme"])
173 // not a valid protocol
174 $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"\n';
178 if($URI_PARTS["scheme"] == "https")
180 // SJM - if they really want curl, or it isn't PHP 4.3 yet, or openssl extension isn't loaded
182 if($use_curl || !function_exists('file_get_contents') || !extension_loaded('openssl'))
184 if(!$this->curl_path || (!is_executable($this->curl_path))) {
185 $this->error = "Bad curl ($this->curl_path), can't fetch HTTPS \n";
188 $this->host = $URI_PARTS["host"];
189 if(!empty($URI_PARTS["port"]))
190 $this->port = $URI_PARTS["port"];
193 // using proxy, send entire URI
194 $this->_curlrequest($URI,$URI,$this->_httpmethod);
198 $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : "");
199 // no proxy, send only the path
200 $this->_curlrequest($path, $URI, $this->_httpmethod);
203 if($this->_redirectaddr)
205 /* url was redirected, check if we've hit the max depth */
206 if($this->maxredirs > $this->_redirectdepth)
208 // only follow redirect if it's on this site, or offsiteok is true
209 if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
211 /* follow the redirect */
212 $this->_redirectdepth++;
213 $this->lastredirectaddr=$this->_redirectaddr;
214 $this->fetch($this->_redirectaddr);
219 if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
221 $frameurls = $this->_frameurls;
222 $this->_frameurls = array();
224 while(list(,$frameurl) = each($frameurls))
226 if($this->_framedepth < $this->maxframes)
228 $this->fetch($frameurl);
229 $this->_framedepth++;
239 // SJM - else drop through and treat https as http
241 $this->host = $URI_PARTS["host"];
242 if(!empty($URI_PARTS["port"]))
243 $this->port = $URI_PARTS["port"];
245 // SJM - if it's https, default the port to 443
246 if($URI_PARTS["scheme"] == "https")
248 if(empty($URI_PARTS["port"]))
254 if($this->_connect($fp))
258 // using proxy, send entire URI
259 $this->_httprequest($URI,$fp,$URI,$this->_httpmethod);
263 $path = $URI_PARTS["path"].(isset($URI_PARTS["query"]) ? "?".$URI_PARTS["query"] : "");
264 // no proxy, send only the path
265 $this->_httprequest($path, $fp, $URI, $this->_httpmethod);
268 $this->_disconnect($fp);
270 if($this->_redirectaddr)
272 /* url was redirected, check if we've hit the max depth */
273 if($this->maxredirs > $this->_redirectdepth)
275 // only follow redirect if it's on this site, or offsiteok is true
276 if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
278 /* follow the redirect */
279 $this->_redirectdepth++;
280 $this->lastredirectaddr=$this->_redirectaddr;
281 $this->fetch($this->_redirectaddr);
286 if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
288 $frameurls = $this->_frameurls;
289 $this->_frameurls = array();
291 while(list(,$frameurl) = each($frameurls))
293 if($this->_framedepth < $this->maxframes)
295 $this->fetch($frameurl);
296 $this->_framedepth++;
312 /*======================================================================*\
314 \*======================================================================*/
317 /*======================================================================*\
318 Function: _striplinks
319 Purpose: strip the hyperlinks from an html document
320 Input: $document document to strip.
321 Output: $match an array of the links
322 \*======================================================================*/
324 function _striplinks($document)
326 preg_match_all("'<\s*a\s+.*href\s*=\s* # find <a href=
327 ([\"\'])? # find single or double quote
328 (?(1) (.*?)\\1 | ([^\s\>]+)) # if quote found, match up to next matching
329 # quote, otherwise match up to next space
330 'isx",$document,$links);
333 // catenate the non-empty matches from the conditional subpattern
335 while(list($key,$val) = each($links[2]))
341 while(list($key,$val) = each($links[3]))
351 /*======================================================================*\
353 Purpose: strip the form elements from an html document
354 Input: $document document to strip.
355 Output: $match an array of the links
356 \*======================================================================*/
358 function _stripform($document)
360 preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))'Usi",$document,$elements);
362 // catenate the matches
363 $match = implode("\r\n",$elements[0]);
371 /*======================================================================*\
373 Purpose: strip the text from an html document
374 Input: $document document to strip.
375 Output: $text the resulting text
376 \*======================================================================*/
378 function _striptext($document)
381 // I didn't use preg eval (//e) since that is only available in PHP 4.0.
382 // so, list your entities one by one here. I included some of the
385 $search = array("'<script[^>]*?>.*?</script>'si", // strip out javascript
386 "'<[\/\!]*?[^<>]*?>'si", // strip out html tags
387 "'([\r\n])[\s]+'", // strip out white space
388 "'&(quote|#34);'i", // replace html entities
398 $replace = array( "",
411 $text = preg_replace($search,$replace,$document);
416 /*======================================================================*\
417 Function: _expandlinks
418 Purpose: expand each link into a fully qualified URL
419 Input: $links the links to qualify
420 $URI the full URI to get the base from
421 Output: $expandedLinks the expanded links
422 \*======================================================================*/
424 function _expandlinks($links,$URI)
427 preg_match("/^[^\?]+/",$URI,$match);
429 $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|","",$match[0]);
431 $search = array( "|^http://".preg_quote($this->host)."|i",
432 "|^(?!http://)(\/)?(?!mailto:)|i",
437 $replace = array( "",
443 $expandedLinks = preg_replace($search,$replace,$links);
445 return $expandedLinks;
448 /*======================================================================*\
449 Function: _httprequest
450 Purpose: go get the http data from the server
451 Input: $url the url to fetch
452 $fp the current open file pointer
454 $body body contents to send if any (POST)
456 \*======================================================================*/
458 function _httprequest($url,$fp,$URI,$http_method,$content_type="",$body="")
460 if($this->passcookies && $this->_redirectaddr)
463 $URI_PARTS = parse_url($URI);
466 $headers = $http_method." ".$url." ".$this->_httpversion."\r\n";
467 if(!empty($this->agent))
468 $headers .= "User-Agent: ".$this->agent."\r\n";
469 if(!empty($this->host) && !isset($this->rawheaders['Host']))
470 $headers .= "Host: ".$this->host."\r\n";
471 if(!empty($this->accept))
472 $headers .= "Accept: ".$this->accept."\r\n";
474 if($this->use_gzip) {
475 // make sure PHP was built with --with-zlib
476 // and we can handle gzipp'ed data
477 if ( function_exists(gzinflate) ) {
478 $headers .= "Accept-encoding: gzip\r\n";
482 "use_gzip is on, but PHP was built without zlib support.".
483 " Requesting file(s) without gzip encoding.",
488 if(!empty($this->referer))
489 $headers .= "Referer: ".$this->referer."\r\n";
490 if(!empty($this->cookies))
492 if(!is_array($this->cookies))
493 $this->cookies = (array)$this->cookies;
495 reset($this->cookies);
496 if ( count($this->cookies) > 0 ) {
497 $cookie_headers .= 'Cookie: ';
498 foreach ( $this->cookies as $cookieKey => $cookieVal ) {
499 $cookie_headers .= $cookieKey."=".urlencode($cookieVal)."; ";
501 $headers .= substr($cookie_headers,0,-2) . "\r\n";
504 if(!empty($this->rawheaders))
506 if(!is_array($this->rawheaders))
507 $this->rawheaders = (array)$this->rawheaders;
508 while(list($headerKey,$headerVal) = each($this->rawheaders))
509 $headers .= $headerKey.": ".$headerVal;
511 if(!empty($content_type)) {
512 $headers .= "Content-type: $content_type";
513 if ($content_type == "multipart/form-data")
514 $headers .= "; boundary=".$this->_mime_boundary;
518 $headers .= "Content-length: ".strlen($body)."\r\n";
519 if(!empty($this->user) || !empty($this->pass))
520 $headers .= "Authorization: Basic ".base64_encode($this->user.":".$this->pass)."\r\n";
524 // set the read timeout if needed
525 if ($this->read_timeout > 0)
526 socket_set_timeout($fp, $this->read_timeout);
527 $this->timed_out = false;
529 fwrite($fp,$headers.$body,strlen($headers.$body));
531 $this->_redirectaddr = false;
532 unset($this->headers);
534 // content was returned gzip encoded?
537 while($currentHeader = fgets($fp,$this->_maxlinelen))
539 if ($this->read_timeout > 0 && $this->_check_timeout($fp))
545 // if($currentHeader == "\r\n")
546 if(preg_match("/^\r?\n$/", $currentHeader) )
549 if(!$this->_tried_digest && preg_match("/^WWW-Authenticate: Digest (.*)/", $currentHeader, $matches))
551 // SJM - we got a Digest challenge. Try to respond...
553 $digestheader = $matches[1];
555 preg_match("/nonce=\"(.*?)\"/", $digestheader, $matches);
556 $nonce = $matches[1];
558 preg_match("/realm=\"(.*?)\"/", $digestheader, $matches);
559 $realm = $matches[1];
561 $cnonce = md5(microtime());
563 $a1 = $this->user . ":" . $realm . ":" . $this->pass;
564 $a2 = $http_method . ":" . $url;
569 $response = md5($ha1 . ":" . $nonce . ":00000001:" . $cnonce . ":auth:" . $ha2);
571 $auth = 'Digest username="' . $this->user . '", ';
572 $auth .= 'realm="' . $realm . '", ';
573 $auth .= 'nonce="' . $nonce . '", ';
574 $auth .= 'uri="' . $url . '", ';
575 $auth .= 'response="' . $response . '", ';
576 $auth .= 'algorithm="MD5", ';
577 $auth .= 'cnonce="' . $cnonce . '", ';
578 $auth .= 'nc=00000001, ';
579 $auth .= 'qop="auth"';
581 // SJM - treat Digest challenge as a redirect. set flag so we don't keep retrying.
583 $this->_tried_digest = true;
585 $this->rawheaders["Authorization"]=$auth . "\r\n";
589 $this->_redirectaddr = $URI_PARTS['scheme'] . '://' . $this->host . $url;
592 // if a header begins with Location: or URI:, set the redirect
593 if(preg_match("/^(Location:|URI:)/i",$currentHeader))
595 // get URL portion of the redirect
596 preg_match("/^(Location:|URI:)\s+(.*)/",chop($currentHeader),$matches);
597 // look for :// in the Location header to see if hostname is included
598 if(!preg_match("|\:\/\/|",$matches[2]))
600 // no host in the path, so prepend
601 $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port;
602 // eliminate double slash
603 if(!preg_match("|^/|",$matches[2]))
604 $this->_redirectaddr .= "/".$matches[2];
606 $this->_redirectaddr .= $matches[2];
609 $this->_redirectaddr = $matches[2];
612 if(preg_match("|^HTTP/|",$currentHeader))
614 if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status))
616 $this->status= $status[1];
618 $this->response_code = $currentHeader;
621 if (preg_match("/Content-Encoding: gzip/", $currentHeader) ) {
625 $this->headers[] = $currentHeader;
628 # $results = fread($fp, $this->maxlength);
630 while ( $data = fread($fp, $this->maxlength) ) {
633 strlen($results) > $this->maxlength ) {
640 // per http://www.php.net/manual/en/function.gzencode.php
641 $results = substr($results, 10);
642 $results = gzinflate($results);
645 if ($this->read_timeout > 0 && $this->_check_timeout($fp))
651 // check if there is a a redirect meta tag
653 if(preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]+URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match))
655 $this->_redirectaddr = $this->_expandlinks($match[1],$URI);
658 // have we hit our frame depth and is there frame src to fetch?
659 if(($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\"\>]+)'i",$results,$match))
661 $this->results[] = $results;
662 for($x=0; $x<count($match[1]); $x++)
663 $this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host);
665 // have we already fetched framed content?
666 elseif(is_array($this->results))
667 $this->results[] = $results;
670 $this->results = $results;
675 /*======================================================================*\
676 Function: _curlrequest
677 Purpose: go get the https data from the server using curl
678 Input: $url the url to fetch
680 $body body contents to send if any (POST)
682 \*======================================================================*/
684 function _curlrequest($url,$URI,$http_method,$content_type="",$body="")
686 if($this->passcookies && $this->_redirectaddr)
691 $URI_PARTS = parse_url($URI);
694 // GET ... header not needed for curl
695 //$headers[] = $http_method." ".$url." ".$this->_httpversion;
696 if(!empty($this->agent))
697 $headers[] = "User-Agent: ".$this->agent;
698 if(!empty($this->host))
699 $headers[] = "Host: ".$this->host;
700 if(!empty($this->accept))
701 $headers[] = "Accept: ".$this->accept;
702 if(!empty($this->referer))
703 $headers[] = "Referer: ".$this->referer;
704 if(!empty($this->cookies))
706 if(!is_array($this->cookies))
707 $this->cookies = (array)$this->cookies;
709 reset($this->cookies);
710 if ( count($this->cookies) > 0 ) {
711 $cookie_str = 'Cookie: ';
712 foreach ( $this->cookies as $cookieKey => $cookieVal ) {
713 $cookie_str .= $cookieKey."=".urlencode($cookieVal)."; ";
715 $headers[] = substr($cookie_str,0,-2);
718 if(!empty($this->rawheaders))
720 if(!is_array($this->rawheaders))
721 $this->rawheaders = (array)$this->rawheaders;
722 while(list($headerKey,$headerVal) = each($this->rawheaders))
723 $headers[] = $headerKey.": ".$headerVal;
725 if(!empty($content_type)) {
726 if ($content_type == "multipart/form-data")
727 $headers[] = "Content-type: $content_type; boundary=".$this->_mime_boundary;
729 $headers[] = "Content-type: $content_type";
732 $headers[] = "Content-length: ".strlen($body);
733 if(!empty($this->user) || !empty($this->pass))
734 $headers[] = "Authorization: BASIC ".base64_encode($this->user.":".$this->pass);
736 for($curr_header = 0; $curr_header < count($headers); $curr_header++)
737 $cmdline_params .= " -H \"".$headers[$curr_header]."\"";
740 $cmdline_params .= " -d \"$body\"";
742 if($this->read_timeout > 0)
743 $cmdline_params .= " -m ".$this->read_timeout;
745 $headerfile = uniqid(time());
747 # accept self-signed certs
749 // mbi: removed, as it breaks on older cURL's
750 //$cmdline_params .= " -k";
752 exec($this->curl_path." -D \"/tmp/$headerfile\"".$cmdline_params." ".$URI,$results,$return);
756 $this->error = "Error: cURL could not retrieve the document, error $return.";
761 $results = implode("\r\n",$results);
763 $result_headers = file("/tmp/$headerfile");
765 $this->_redirectaddr = false;
766 unset($this->headers);
768 for($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++)
771 // if a header begins with Location: or URI:, set the redirect
772 if(preg_match("/^(Location: |URI: )/i",$result_headers[$currentHeader]))
774 // get URL portion of the redirect
775 preg_match("/^(Location: |URI:)(.*)/",chop($result_headers[$currentHeader]),$matches);
776 // look for :// in the Location header to see if hostname is included
777 if(!preg_match("|\:\/\/|",$matches[2]))
779 // no host in the path, so prepend
780 $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port;
781 // eliminate double slash
782 if(!preg_match("|^/|",$matches[2]))
783 $this->_redirectaddr .= "/".$matches[2];
785 $this->_redirectaddr .= $matches[2];
788 $this->_redirectaddr = $matches[2];
791 if(preg_match("|^HTTP/|",$result_headers[$currentHeader]))
793 $this->response_code = $result_headers[$currentHeader];
794 if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$this->response_code, $match))
796 $this->status= $match[1];
799 $this->headers[] = $result_headers[$currentHeader];
802 // check if there is a a redirect meta tag
804 if(preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]+URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match))
806 $this->_redirectaddr = $this->_expandlinks($match[1],$URI);
809 // have we hit our frame depth and is there frame src to fetch?
810 if(($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\"\>]+)'i",$results,$match))
812 $this->results[] = $results;
813 for($x=0; $x<count($match[1]); $x++)
814 $this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host);
816 // have we already fetched framed content?
817 elseif(is_array($this->results))
818 $this->results[] = $results;
821 $this->results = $results;
823 unlink("/tmp/$headerfile");
828 /*======================================================================*\
829 Function: setcookies()
830 Purpose: set cookies for a redirection
831 \*======================================================================*/
833 function setcookies()
835 for($x=0; $x<count($this->headers); $x++)
837 if(preg_match("/^set-cookie:[\s]+([^=]+)=([^;]+)/i", $this->headers[$x],$match))
838 $this->cookies[$match[1]] = $match[2];
843 /*======================================================================*\
844 Function: _check_timeout
845 Purpose: checks whether timeout has occurred
846 Input: $fp file pointer
847 \*======================================================================*/
849 function _check_timeout($fp)
851 if ($this->read_timeout > 0) {
852 $fp_status = socket_get_status($fp);
853 if ($fp_status["timed_out"]) {
854 $this->timed_out = true;
861 /*======================================================================*\
863 Purpose: make a socket connection
864 Input: $fp file pointer
865 \*======================================================================*/
867 function _connect(&$fp)
869 if(!empty($this->proxy_host) && !empty($this->proxy_port))
871 $this->_isproxy = true;
872 $host = $this->proxy_host;
873 $port = $this->proxy_port;
883 if($this->_scheme == "https")
885 $host = "ssl://" . $host;
896 // socket connection succeeded
902 // socket connection failed
903 $this->status = $errno;
907 $this->error="socket creation failed (-3)";
909 $this->error="dns lookup failure (-4)";
911 $this->error="connection refused or timed out (-5)";
913 $this->error="connection failed (".$errno.")";
918 /*======================================================================*\
919 Function: _disconnect
920 Purpose: disconnect a socket connection
921 Input: $fp file pointer
922 \*======================================================================*/
924 function _disconnect($fp)
930 /*======================================================================*\
931 Function: _prepare_post_body
932 Purpose: Prepare post body according to encoding type
933 Input: $formvars - form variables
934 $formfiles - form upload files
936 \*======================================================================*/
938 function _prepare_post_body($formvars, $formfiles)
940 settype($formvars, "array");
941 settype($formfiles, "array");
943 if (count($formvars) == 0 && count($formfiles) == 0)
946 switch ($this->_submit_type) {
947 case "application/x-www-form-urlencoded":
949 while(list($key,$val) = each($formvars)) {
950 if (is_array($val) || is_object($val)) {
951 while (list($cur_key, $cur_val) = each($val)) {
952 $postdata .= urlencode($key)."[]=".urlencode($cur_val)."&";
955 $postdata .= urlencode($key)."=".urlencode($val)."&";
959 case "multipart/form-data":
960 $this->_mime_boundary = "Snoopy".md5(uniqid(microtime()));
963 while(list($key,$val) = each($formvars)) {
964 if (is_array($val) || is_object($val)) {
965 while (list($cur_key, $cur_val) = each($val)) {
966 $postdata .= "--".$this->_mime_boundary."\r\n";
967 $postdata .= "Content-Disposition: form-data; name=\"$key\[\]\"\r\n\r\n";
968 $postdata .= "$cur_val\r\n";
971 $postdata .= "--".$this->_mime_boundary."\r\n";
972 $postdata .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n";
973 $postdata .= "$val\r\n";
978 while (list($field_name, $file_names) = each($formfiles)) {
979 settype($file_names, "array");
980 while (list(, $file_name) = each($file_names)) {
981 if (!is_readable($file_name)) continue;
983 $fp = fopen($file_name, "r");
984 $file_content = fread($fp, filesize($file_name));
986 $base_name = basename($file_name);
988 $postdata .= "--".$this->_mime_boundary."\r\n";
989 $postdata .= "Content-Disposition: form-data; name=\"$field_name\"; filename=\"$base_name\"\r\n\r\n";
990 $postdata .= "$file_content\r\n";
993 $postdata .= "--".$this->_mime_boundary."--\r\n";