root/trunk/gregarius/extlib/Snoopy.class.inc

Revision 1477, 30.3 kB (checked in by sdcosta, 2 years ago)

Fix for #409 The website was sending the gzip header as
"Content-encoding: gzip" instead of "Content-Encoding: gzip". If firefox fixes it, then so should we.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2
3/*************************************************
4
5Snoopy - the PHP net client
6Author: Monte Ohrt <monte@ispi.net>
7Copyright (c): 1999-2000 ispi, all rights reserved
8Version: 1.0 (plus - see SJM comments below)
9
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.
14 *
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.
19 *
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
23
24You may contact the author of Snoopy by e-mail at:
25monte@ispi.net
26
27Or, write to:
28Monte Ohrt
29CTO, ispi
30237 S. 70th suite 220
31Lincoln, NE 68510
32
33The latest version of Snoopy can be obtained from:
34http://snoopy.sourceforge.com
35
36
37
38SJM - alpha-grade changes based on the version of Snoopy released with MagpieRSS 0.7
39
40comments to steve@minutillo.com
41
42Two additions:
43
441) 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.
47   
48   ref:  http://us2.php.net/fsockopen
49   
502) 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.
56   
57   ref:  http://www.faqs.org/rfcs/rfc2617.html
58
59*************************************************/
60
61class Snoopy
62{
63        /**** Public variables ****/
64       
65        /* user definable vars */
66
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";
77
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()
84                                                                                                // or submitlinks()
85        var $passcookies        =       true;                           // pass set cookies back through redirects
86                                                                                                // NOTE: this currently does not respect
87                                                                                                // dates, domains or paths.
88       
89        var     $user                   =       "";                                     // user for http authentication
90        var     $pass                   =       "";                                     // password for http authentication
91       
92        // http accept types
93        var $accept                     =       "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*";
94       
95        var $results            =       "";                                     // where the content is put
96               
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
106       
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.
118       
119        // SJM - always use curl for HTTPS requests?
120        var $use_curl           = false;       
121       
122
123        // send Accept-encoding: gzip?
124        var $use_gzip           = true;
125       
126        /**** Private variables ****/   
127       
128        var     $_maxlinelen    =       4096;                           // max line length (headers)
129       
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
141       
142        var $_isproxy           =       false;                          // set if using a proxy server
143        var $_fp_timeout        =       30;                                     // timeout for socket connection
144        var $tmpdir = "/tmp/";
145
146        function Snoopy() {
147                        if (defined('CURL_PATH')) {
148                                        $this -> curl_path = CURL_PATH;
149                                        $this -> use_curl = true;
150                        }
151                        /*
152                        if (function_exists('getConfig') && ($tmp = getConfig('rss.output.cachedir')) != null) {
153                                        if (substr($tmp, -1, 1) != "/") {
154                                                $tmp .= "/";
155                                        }
156                                        $this -> tmpdir = $tmp;
157                        }
158                        */
159        }
160/*======================================================================*\
161        Function:       fetch
162        Purpose:        fetch the contents of a web page
163                                (and possibly other protocols in the
164                                future like ftp, nntp, gopher, etc.)
165        Input:          $URI    the location of the page to fetch
166        Output:         $this->results  the output text from the fetch
167\*======================================================================*/
168
169        function fetch($URI)
170        {
171       
172                //preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS);
173                $URI_PARTS = parse_url($URI);
174                if (!empty($URI_PARTS["user"]))
175                        $this->user = $URI_PARTS["user"];
176                if (!empty($URI_PARTS["pass"]))
177                        $this->pass = $URI_PARTS["pass"];
178               
179                $this->_scheme = $URI_PARTS["scheme"];
180
181                switch($URI_PARTS["scheme"])
182                {
183                        case "http":
184                        case "https":
185                                break;
186                               
187                        default:
188                                // not a valid protocol
189                                $this->error    =       'Invalid protocol "'.$URI_PARTS["scheme"].'"\n';
190                                return false;
191                }
192               
193                if($URI_PARTS["scheme"] == "https")
194                {
195                       
196                        // SJM - if they really want curl, or it isn't PHP 4.3 yet, or openssl extension isn't loaded                   
197                        if($use_curl || !function_exists('file_get_contents') || !extension_loaded('openssl'))
198                        {
199                               
200                                // MBI: is_executable would break on some installs
201                                if(!$this->curl_path || (!file_exists($this->curl_path))) {                                     
202                                        $this->error = "Bad curl ($this->curl_path), can't fetch HTTPS \n";                                                     
203                                        return false;
204                                }
205                       
206                                $this->host = $URI_PARTS["host"];
207                                if(!empty($URI_PARTS["port"]))
208                                        $this->port = $URI_PARTS["port"];
209                                if($this->_isproxy)
210                                {
211                                        // using proxy, send entire URI
212                                        $this->_curlrequest($URI,$URI,$this->_httpmethod);
213                                }
214                                else
215                                {
216                                        $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : "");
217                                        // no proxy, send only the path
218                                        $this->_curlrequest($path, $URI, $this->_httpmethod);
219                                }
220
221                                if($this->_redirectaddr)
222                                {
223                                        /* url was redirected, check if we've hit the max depth */
224                                        if($this->maxredirs > $this->_redirectdepth)
225                                        {
226                                                // only follow redirect if it's on this site, or offsiteok is true
227                                                if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
228                                                {
229                                                        /* follow the redirect */
230                                                        $this->_redirectdepth++;
231                                                        $this->lastredirectaddr=$this->_redirectaddr;
232                                                        $this->fetch($this->_redirectaddr);
233                                                }
234                                        }
235                                }
236
237                                if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
238                                {
239                                        $frameurls = $this->_frameurls;
240                                        $this->_frameurls = array();
241
242                                        while(list(,$frameurl) = each($frameurls))
243                                        {
244                                                if($this->_framedepth < $this->maxframes)
245                                                {
246                                                        $this->fetch($frameurl);
247                                                        $this->_framedepth++;
248                                                }
249                                                else
250                                                        break;
251                                        }
252                                }                                       
253                                return true;                                   
254                        }
255                }
256
257                // SJM - else drop through and treat https as http
258               
259                $this->host = $URI_PARTS["host"];
260                if(!empty($URI_PARTS["port"]))
261                        $this->port = $URI_PARTS["port"];
262               
263                // SJM - if it's https, default the port to 443
264                if($URI_PARTS["scheme"] == "https")
265                {
266                        if(empty($URI_PARTS["port"]))
267                        {
268                                $this->port = 443;
269                        }
270                }
271               
272                if($this->_connect($fp))
273                {
274                        if($this->_isproxy)
275                        {
276                                // using proxy, send entire URI
277                                $this->_httprequest($URI,$fp,$URI,$this->_httpmethod);
278                        }
279                        else
280                        {
281                                $path = $URI_PARTS["path"].(isset($URI_PARTS["query"]) ? "?".$URI_PARTS["query"] : "");
282                                // no proxy, send only the path
283                                $this->_httprequest($path, $fp, $URI, $this->_httpmethod);
284                        }
285
286                        $this->_disconnect($fp);
287
288                        if($this->_redirectaddr)
289                        {
290                                /* url was redirected, check if we've hit the max depth */
291                                if($this->maxredirs > $this->_redirectdepth)
292                                {
293                                        // only follow redirect if it's on this site, or offsiteok is true
294                                        if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok)
295                                        {
296                                                /* follow the redirect */
297                                                $this->_redirectdepth++;
298                                                $this->lastredirectaddr=$this->_redirectaddr;
299                                                $this->fetch($this->_redirectaddr);
300                                        }
301                                }
302                        }
303
304                        if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0)
305                        {
306                                $frameurls = $this->_frameurls;
307                                $this->_frameurls = array();
308
309                                while(list(,$frameurl) = each($frameurls))
310                                {
311                                        if($this->_framedepth < $this->maxframes)
312                                        {
313                                                $this->fetch($frameurl);
314                                                $this->_framedepth++;
315                                        }
316                                        else
317                                                break;
318                                }
319                        }                                       
320                }
321                else
322                {
323                        return false;
324                }
325                return true;                                   
326        }
327
328
329
330/*======================================================================*\
331        Private functions
332\*======================================================================*/
333       
334       
335/*======================================================================*\
336        Function:       _striplinks
337        Purpose:        strip the hyperlinks from an html document
338        Input:          $document       document to strip.
339        Output:         $match          an array of the links
340\*======================================================================*/
341
342        function _striplinks($document)
343        {       
344                preg_match_all("'<\s*a\s+.*href\s*=\s*                  # find <a href=
345                                                ([\"\'])?                                       # find single or double quote
346                                                (?(1) (.*?)\\1 | ([^\s\>]+))            # if quote found, match up to next matching
347                                                                                                        # quote, otherwise match up to next space
348                                                'isx",$document,$links);
349                                               
350
351                // catenate the non-empty matches from the conditional subpattern
352
353                while(list($key,$val) = each($links[2]))
354                {
355                        if(!empty($val))
356                                $match[] = $val;
357                }                               
358               
359                while(list($key,$val) = each($links[3]))
360                {
361                        if(!empty($val))
362                                $match[] = $val;
363                }               
364               
365                // return the links
366                return $match;
367        }
368
369/*======================================================================*\
370        Function:       _stripform
371        Purpose:        strip the form elements from an html document
372        Input:          $document       document to strip.
373        Output:         $match          an array of the links
374\*======================================================================*/
375
376        function _stripform($document)
377        {       
378                preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))'Usi",$document,$elements);
379               
380                // catenate the matches
381                $match = implode("\r\n",$elements[0]);
382                               
383                // return the links
384                return $match;
385        }
386
387       
388       
389/*======================================================================*\
390        Function:       _striptext
391        Purpose:        strip the text from an html document
392        Input:          $document       document to strip.
393        Output:         $text           the resulting text
394\*======================================================================*/
395
396        function _striptext($document)
397        {
398               
399                // I didn't use preg eval (//e) since that is only available in PHP 4.0.
400                // so, list your entities one by one here. I included some of the
401                // more common ones.
402                                                               
403                $search = array("'<script[^>]*?>.*?</script>'si",       // strip out javascript
404                                                "'<[\/\!]*?[^<>]*?>'si",                        // strip out html tags
405                                                "'([\r\n])[\s]+'",                                      // strip out white space
406                                                "'&(quote|#34);'i",                                     // replace html entities
407                                                "'&(amp|#38);'i",
408                                                "'&(lt|#60);'i",
409                                                "'&(gt|#62);'i",
410                                                "'&(nbsp|#160);'i",
411                                                "'&(iexcl|#161);'i",
412                                                "'&(cent|#162);'i",
413                                                "'&(pound|#163);'i",
414                                                "'&(copy|#169);'i"
415                                                );                             
416                $replace = array(       "",
417                                                        "",
418                                                        "\\1",
419                                                        "\"",
420                                                        "&",
421                                                        "<",
422                                                        ">",
423                                                        " ",
424                                                        chr(161),
425                                                        chr(162),
426                                                        chr(163),
427                                                        chr(169));
428                                       
429                $text = preg_replace($search,$replace,$document);
430                                                               
431                return $text;
432        }
433
434/*======================================================================*\
435        Function:       _expandlinks
436        Purpose:        expand each link into a fully qualified URL
437        Input:          $links                  the links to qualify
438                                $URI                    the full URI to get the base from
439        Output:         $expandedLinks  the expanded links
440\*======================================================================*/
441
442        function _expandlinks($links,$URI)
443        {
444               
445                preg_match("/^[^\?]+/",$URI,$match);
446
447                $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|","",$match[0]);
448                               
449                $search = array(        "|^http://".preg_quote($this->host)."|i",
450                                                        "|^(?!http://)(\/)?(?!mailto:)|i",
451                                                        "|/\./|",
452                                                        "|/[^\/]+/\.\./|"
453                                                );
454                                               
455                $replace = array(       "",
456                                                        $match."/",
457                                                        "/",
458                                                        "/"
459                                                );                     
460                               
461                $expandedLinks = preg_replace($search,$replace,$links);
462
463                return $expandedLinks;
464        }
465
466/*======================================================================*\
467        Function:       _httprequest
468        Purpose:        go get the http data from the server
469        Input:          $url            the url to fetch
470                                $fp                     the current open file pointer
471                                $URI            the full URI
472                                $body           body contents to send if any (POST)
473        Output:         
474\*======================================================================*/
475       
476        function _httprequest($url,$fp,$URI,$http_method,$content_type="",$body="")
477        {
478                if($this->passcookies && $this->_redirectaddr)
479                        $this->setcookies();
480                       
481                $URI_PARTS = parse_url($URI);
482                if(empty($url))
483                        $url = "/";
484                $headers = $http_method." ".$url." ".$this->_httpversion."\r\n";               
485                if(!empty($this->agent))
486                        $headers .= "User-Agent: ".$this->agent."\r\n";
487                if(!empty($this->host) && !isset($this->rawheaders['Host']))
488                        $headers .= "Host: ".$this->host."\r\n";
489                if(!empty($this->accept))
490                        $headers .= "Accept: ".$this->accept."\r\n";
491               
492                if($this->use_gzip) {
493                        // make sure PHP was built with --with-zlib
494                        // and we can handle gzipp'ed data
495                        if ( function_exists(gzinflate) ) {
496                           $headers .= "Accept-encoding: gzip\r\n";
497                        }
498                        else {
499                           trigger_error(
500                                "use_gzip is on, but PHP was built without zlib support.".
501                                "  Requesting file(s) without gzip encoding.",
502                                E_USER_NOTICE);
503                        }
504                }
505               
506                if(!empty($this->referer))
507                        $headers .= "Referer: ".$this->referer."\r\n";
508                if(!empty($this->cookies))
509                {                       
510                        if(!is_array($this->cookies))
511                                $this->cookies = (array)$this->cookies;
512       
513                        reset($this->cookies);
514                        if ( count($this->cookies) > 0 ) {
515                                $cookie_headers .= 'Cookie: ';
516                                foreach ( $this->cookies as $cookieKey => $cookieVal ) {
517                                $cookie_headers .= $cookieKey."=".urlencode($cookieVal)."; ";
518                                }
519                                $headers .= substr($cookie_headers,0,-2) . "\r\n";
520                        }
521                }
522                if(!empty($this->rawheaders))
523                {
524                        if(!is_array($this->rawheaders))
525                                $this->rawheaders = (array)$this->rawheaders;
526                        while(list($headerKey,$headerVal) = each($this->rawheaders))
527                                $headers .= $headerKey.": ".$headerVal."\r\n";
528                }
529                if(!empty($content_type)) {
530                        $headers .= "Content-type: $content_type";
531                        if ($content_type == "multipart/form-data")
532                                $headers .= "; boundary=".$this->_mime_boundary;
533                        $headers .= "\r\n";
534                }
535                if(!empty($body))       
536                        $headers .= "Content-length: ".strlen($body)."\r\n";
537                if(!empty($this->user) || !empty($this->pass)) 
538                        $headers .= "Authorization: BASIC ".base64_encode(urldecode($this->user).":".urldecode($this->pass))."\r\n";
539
540                $headers .= "\r\n";
541               
542                // set the read timeout if needed
543                if ($this->read_timeout > 0)
544                        socket_set_timeout($fp, $this->read_timeout);
545                $this->timed_out = false;
546               
547                fwrite($fp,$headers.$body,strlen($headers.$body));
548               
549                $this->_redirectaddr = false;
550                unset($this->headers);
551               
552                // content was returned gzip encoded?
553                $is_gzipped = false;
554                                               
555                while($currentHeader = fgets($fp,$this->_maxlinelen))
556                {
557                        if ($this->read_timeout > 0 && $this->_check_timeout($fp))
558                        {
559                                $this->status=-100;
560                                return false;
561                        }
562                               
563                //      if($currentHeader == "\r\n")
564                        if(preg_match("/^\r?\n$/", $currentHeader) )
565                              break;
566
567                        if(!$this->_tried_digest && preg_match("/^WWW-Authenticate: Digest (.*)/", $currentHeader, $matches))
568                        {
569                                // SJM - we got a Digest challenge.  Try to respond...
570                               
571                                $digestheader = $matches[1];
572                               
573                                preg_match("/nonce=\"(.*?)\"/", $digestheader, $matches);
574                                $nonce = $matches[1];
575
576                                preg_match("/realm=\"(.*?)\"/", $digestheader, $matches);
577                                $realm = $matches[1];
578
579                                $cnonce = md5(microtime());
580
581                                $a1 = $this->user . ":" . $realm . ":" . $this->pass;
582                                $a2 = $http_method . ":" . $url;
583
584                                $ha1 = md5($a1);
585                                $ha2 = md5($a2);
586
587                                $response = md5($ha1 . ":" . $nonce . ":00000001:" . $cnonce . ":auth:" . $ha2);
588
589                                $auth  = 'Digest username="' . $this->user . '", ';
590                                $auth .= 'realm="' . $realm . '", ';
591                                $auth .= 'nonce="' . $nonce . '", ';
592                                $auth .= 'uri="' . $url . '", ';
593                                $auth .= 'response="' . $response . '", ';
594                                $auth .= 'algorithm="MD5", ';
595                                $auth .= 'cnonce="' . $cnonce . '", ';
596                                $auth .= 'nc=00000001, ';
597                                $auth .= 'qop="auth"';
598
599                                // SJM - treat Digest challenge as a redirect.  set flag so we don't keep retrying.
600                               
601                                $this->_tried_digest = true;
602                               
603                                $this->rawheaders["Authorization"]=$auth;
604                                $this->user = "";
605                                $this->pass = "";
606                               
607                                $this->_redirectaddr = $URI_PARTS['scheme'] . '://' . $this->host . $url;
608                        }
609
610                        // if a header begins with Location: or URI:, set the redirect
611                        if(preg_match("/^(Location:|URI:)/i",$currentHeader))
612                        {
613                                // get URL portion of the redirect
614                                preg_match("/^(Location:|URI:)\s+(.*)/i",chop($currentHeader),$matches);
615                                // look for :// in the Location header to see if hostname is included
616                                if(!preg_match("|\:\/\/|",$matches[2]))
617                                {
618                                        // no host in the path, so prepend
619                                        $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port;
620                                        // eliminate double slash
621                                        if(!preg_match("|^/|",$matches[2]))
622                                                        $this->_redirectaddr .= "/".$matches[2];
623                                        else
624                                                        $this->_redirectaddr .= $matches[2];
625                                }
626                                else
627                                        $this->_redirectaddr = $matches[2];
628                        }
629               
630                        if(preg_match("|^HTTP/|",$currentHeader))
631                        {
632                if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status))
633                                {
634                                        $this->status= $status[1];
635                }                               
636                                $this->response_code = $currentHeader;
637                        }
638                       
639                        if (preg_match("/Content-Encoding: gzip/i", $currentHeader) ) {
640                                $is_gzipped = true;
641                        }
642                       
643                        $this->headers[] = $currentHeader;
644                }
645
646                # $results = fread($fp, $this->maxlength);
647                $results = "";
648                while ( $data = fread($fp, $this->maxlength) ) {
649                    $results .= $data;
650                    if (
651                        strlen($results) > $this->maxlength ) {
652                        break;
653                    }
654                }
655               
656                // gunzip
657                if ( $is_gzipped ) {
658                        // per http://www.php.net/manual/en/function.gzencode.php
659                        $results = substr($results, 10);
660                        $results = gzinflate($results);
661                }
662               
663                if ($this->read_timeout > 0 && $this->_check_timeout($fp))
664                {
665                        $this->status=-100;
666                        return false;
667                }
668               
669                // check if there is a a redirect meta tag
670               
671                if(preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]+URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match))
672                {
673                        $this->_redirectaddr = $this->_expandlinks($match[1],$URI);     
674                }
675
676                // have we hit our frame depth and is there frame src to fetch?
677                if(($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\"\>]+)'i",$results,$match))
678                {
679                        $this->results[] = $results;
680                        for($x=0; $x<count($match[1]); $x++)
681                                $this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host);
682                }
683                // have we already fetched framed content?
684                elseif(is_array($this->results))
685                        $this->results[] = $results;
686                // no framed content
687                else
688                        $this->results = $results;
689               
690                return true;
691        }
692
693/*======================================================================*\
694        Function:       _curlrequest
695        Purpose:        go get the https data from the server using curl
696        Input:          $url            the url to fetch
697                                $URI            the full URI
698                                $body           body contents to send if any (POST)
699        Output:         
700\*======================================================================*/
701       
702        function _curlrequest($url,$URI,$http_method,$content_type="",$body="")
703        {
704                if($this->passcookies && $this->_redirectaddr)
705                        $this->setcookies();
706
707                $headers = array();             
708                                       
709                $URI_PARTS = parse_url($URI);
710                if(empty($url))
711                        $url = "/";
712                // GET ... header not needed for curl
713                //$headers[] = $http_method." ".$url." ".$this->_httpversion;           
714                if(!empty($this->agent))
715                        $headers[] = "User-Agent: ".$this->agent;
716                if(!empty($this->host))
717                        $headers[] = "Host: ".$this->host;
718                if(!empty($this->accept))
719                        $headers[] = "Accept: ".$this->accept;
720                if(!empty($this->referer))
721                        $headers[] = "Referer: ".$this->referer;
722                if(!empty($this->cookies))
723                {                       
724                        if(!is_array($this->cookies))
725                                $this->cookies = (array)$this->cookies;
726       
727                        reset($this->cookies);
728                        if ( count($this->cookies) > 0 ) {
729                                $cookie_str = 'Cookie: ';
730                                foreach ( $this->cookies as $cookieKey => $cookieVal ) {
731                                $cookie_str .= $cookieKey."=".urlencode($cookieVal)."; ";
732                                }
733                                $headers[] = substr($cookie_str,0,-2);
734                        }
735                }
736                if(!empty($this->rawheaders))
737                {
738                        if(!is_array($this->rawheaders))
739                                $this->rawheaders = (array)$this->rawheaders;
740                        while(list($headerKey,$headerVal) = each($this->rawheaders))
741                                $headers[] = $headerKey.": ".$headerVal;
742                }
743                if(!empty($content_type)) {
744                        if ($content_type == "multipart/form-data")
745                                $headers[] = "Content-type: $content_type; boundary=".$this->_mime_boundary;
746                        else
747                                $headers[] = "Content-type: $content_type";
748                }
749                if(!empty($body))       
750                        $headers[] = "Content-length: ".strlen($body);
751                if(!empty($this->user) || !empty($this->pass)) 
752                        $headers[] = "Authorization: BASIC ".base64_encode(urldecode($this->user).":".urldecode($this->pass));
753                       
754                for($curr_header = 0; $curr_header < count($headers); $curr_header++) {
755                        $cmdline_params .= " -H \"".$headers[$curr_header]."\"";
756                }
757               
758                if(!empty($body))
759                        $cmdline_params .= " -d \"$body\"";
760               
761                if($this->read_timeout > 0)
762                        $cmdline_params .= " -m ".$this->read_timeout;
763               
764                $headerfile = uniqid(time());
765               
766                # accept self-signed certs
767                // mbi: removed, as it breaks on older cURL's
768                //$cmdline_params .= " -k";
769                if (defined ('RSS_SNOOPY_EXTRA_PARAMS')) {
770                        $cmdline_params .= (" " . RSS_SNOOPY_EXTRA_PARAMS ." ");
771                }
772                $full_cmdline = $this->curl_path
773                        ." -D \"".$this -> tmpdir
774                        ."$headerfile\""
775                        .escapeshellcmd($cmdline_params)." "
776                        .escapeshellcmd($URI);
777                //die($full_cmdline);
778                exec($full_cmdline,$results,$return);
779                               
780                if($return)
781                {
782                        $this->error = "Error: cURL could not retrieve the document, error $return.";
783                        return false;
784                }
785                       
786                       
787                $results = implode("\r\n",$results);
788               
789                $result_headers = file($this -> tmpdir . $headerfile);
790                                               
791                $this->_redirectaddr = false;
792                unset($this->headers);
793                                               
794                for($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++)
795                {
796                       
797                        // if a header begins with Location: or URI:, set the redirect
798                        if(preg_match("/^(Location: |URI: )/i",$result_headers[$currentHeader]))
799                        {
800                                // get URL portion of the redirect
801                                preg_match("/^(Location: |URI:)(.*)/",chop($result_headers[$currentHeader]),$matches);
802                                // look for :// in the Location header to see if hostname is included
803                                if(!preg_match("|\:\/\/|",$matches[2]))
804                                {
805                                        // no host in the path, so prepend
806                                        $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port;
807                                        // eliminate double slash
808                                        if(!preg_match("|^/|",$matches[2]))
809                                                        $this->_redirectaddr .= "/".$matches[2];
810                                        else
811                                                        $this->_redirectaddr .= $matches[2];
812                                }
813                                else
814                                        $this->_redirectaddr = $matches[2];
815                        }
816               
817                        if(preg_match("|^HTTP/|",$result_headers[$currentHeader]))
818                        {
819                            $this->response_code = $result_headers[$currentHeader];
820                            if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$this->response_code, $match))
821                            {
822                                $this->status= $match[1];
823                            }
824                        }
825                        $this->headers[] = $result_headers[$currentHeader];
826                }
827
828                // check if there is a a redirect meta tag
829               
830                if(preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]+URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match))
831                {
832                        $this->_redirectaddr = $this->_expandlinks($match[1],$URI);     
833                }
834
835                // have we hit our frame depth and is there frame src to fetch?
836                if(($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\"\>]+)'i",$results,$match))
837                {
838                        $this->results[] = $results;
839                        for($x=0; $x<count($match[1]); $x++)
840                                $this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host);
841                }
842                // have we already fetched framed content?
843                elseif(is_array($this->results))
844                        $this->results[] = $results;
845                // no framed content
846                else
847                        $this->results = $results;
848
849                unlink($this -> tmpdir . $headerfile);
850               
851                return true;
852        }
853
854/*======================================================================*\
855        Function:       setcookies()
856        Purpose:        set cookies for a redirection
857\*======================================================================*/
858       
859        function setcookies()
860        {
861                for($x=0; $x<count($this->headers); $x++)
862                {
863                if(preg_match("/^set-cookie:[\s]+([^=]+)=([^;]+)/i", $this->headers[$x],$match))
864                        $this->cookies[$match[1]] = $match[2];
865                }
866        }
867
868       
869/*======================================================================*\
870        Function:       _check_timeout
871        Purpose:        checks whether timeout has occurred
872        Input:          $fp     file pointer
873\*======================================================================*/
874
875        function _check_timeout($fp)
876        {
877                if ($this->read_timeout > 0) {
878                        $fp_status = socket_get_status($fp);
879                        if ($fp_status["timed_out"]) {
880                                $this->timed_out = true;
881                                return true;
882                        }
883                }
884                return false;
885        }
886
887/*======================================================================*\
888        Function:       _connect
889        Purpose:        make a socket connection
890        Input:          $fp     file pointer
891\*======================================================================*/
892       
893        function _connect(&$fp)
894        {
895                if(!empty($this->proxy_host) && !empty($this->proxy_port))
896                        {
897                                $this->_isproxy = true;
898                                $host = $this->proxy_host;
899                                $port = $this->proxy_port;
900                        }
901                else
902                {
903                        $host = $this->host;
904                        $port = $this->port;
905                }
906       
907                $this->status = 0;
908               
909                if($this->_scheme == "https")
910                {
911                        $host = "ssl://" . $host;
912                }
913               
914                if($fp = fsockopen(
915                                        $host,
916                                        $port,
917                                        $errno,
918                                        $errstr,
919                                        $this->_fp_timeout
920                                        ))
921                {
922                        // socket connection succeeded
923
924                        return true;
925                }
926                else
927                {
928                        // socket connection failed
929                        $this->status = $errno;
930                        switch($errno)
931                        {
932                                case -3:
933                                        $this->error="socket creation failed (-3)";
934                                case -4:
935                                        $this->error="dns lookup failure (-4)";
936                                case -5:
937                                        $this->error="connection refused or timed out (-5)";
938                                default:
939                                        $this->error="connection failed (".$errno.")";
940                        }
941                        return false;
942                }
943        }
944/*======================================================================*\
945        Function:       _disconnect
946        Purpose:        disconnect a socket connection
947        Input:          $fp     file pointer
948\*======================================================================*/
949       
950        function _disconnect($fp)
951        {
952                return(fclose($fp));
953        }
954
955       
956/*======================================================================*\
957        Function:       _prepare_post_body
958        Purpose:        Prepare post body according to encoding type
959        Input:          $formvars  - form variables
960                                $formfiles - form upload files
961        Output:         post body
962\*======================================================================*/
963       
964        function _prepare_post_body($formvars, $formfiles)
965        {
966                settype($formvars, "array");
967                settype($formfiles, "array");
968
969                if (count($formvars) == 0 && count($formfiles) == 0)
970                        return;
971               
972                switch ($this->_submit_type) {
973                        case "application/x-www-form-urlencoded":
974                                reset($formvars);
975                                while(list($key,$val) = each($formvars)) {
976                                        if (is_array($val) || is_object($val)) {
977                                                while (list($cur_key, $cur_val) = each($val)) {
978                                                        $postdata .= urlencode($key)."[]=".urlencode($cur_val)."&";
979                                                }
980                                        } else
981                                                $postdata .= urlencode($key)."=".urlencode($val)."&";
982                                }
983                                break;
984
985                        case "multipart/form-data":
986                                $this->_mime_boundary = "Snoopy".md5(uniqid(microtime()));
987                               
988                                reset($formvars);
989                                whi