XCL Web Application Platform 2.5.0
The XoopsCube Legacy Project
Loading...
Searching...
No Matches
snoopy.php
1<?php
12
13
14class snoopy
15{
16 /**** Public variables ****/
17
18 /* user definable vars */
19
20 public $scheme = 'http'; // http or https
21 public $host = 'www.php.net'; // host name we are connecting to
22 public $port = 80; // port we are connecting to
23 public $proxy_host = ''; // proxy host to use
24 public $proxy_port = ''; // proxy port to use
25 public $proxy_user = ''; // proxy user to use
26 public $proxy_pass = ''; // proxy password to use
27
28 public $agent = 'Snoopy v2.0.1'; // agent we masquerade as
29 public $referer = ''; // referer info to pass
30 public $cookies = []; // array of cookies to pass
31 // $cookies["username"]="joe";
32 public $rawheaders = []; // array of raw headers to send
33 // $rawheaders["Content-type"]="text/html";
34
35 public $maxredirs = 5; // http redirection depth maximum. 0 = disallow
36 public $lastredirectaddr = ''; // contains address of last redirected address
37 public $offsiteok = true; // allows redirection off-site
38 public $maxframes = 0; // frame content depth maximum. 0 = disallow
39 public $expandlinks = true; // expand links to fully qualified URLs.
40 // this only applies to fetchlinks()
41 // submitlinks(), and submittext()
42 public $passcookies = true; // pass set cookies back through redirects
43 // NOTE: this currently does not respect
44 // dates, domains or paths.
45
46 public $user = ''; // user for http authentication
47 public $pass = ''; // password for http authentication
48
49 // http accept types
50 public $accept = 'image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*';
51
52 public $results = ''; // where the content is put
53
54 public $error = ''; // error messages sent here
55 public $response_code = ''; // response code returned from server
56 public $headers = []; // headers returned from server sent here
57 public $maxlength = 500000; // max return data length (body)
58 public $read_timeout = 0; // timeout on read operations, in seconds
59 // supported only since PHP 4 Beta 4
60 // set to 0 to disallow timeouts
61 public $timed_out = false; // if a read operation timed out
62 public $status = 0; // http request status
63
64 public $temp_dir = '/tmp'; // temporary directory that the webserver
65 // has permission to write to.
66 // under Windows, this should be C:\temp
67
68 public $curl_path = false;
69 // deprecated, snoopy no longer uses curl for https requests,
70 // but instead requires the openssl extension.
71
72 // send Accept-encoding: gzip?
73 public $use_gzip = true;
74
75 // file or directory with CA certificates to verify remote host with
76 public $cafile;
77 public $capath;
78
79 /**** Private variables ****/
80
81 public $_maxlinelen = 4096; // max line length (headers)
82
83 public $_httpmethod = 'GET'; // default http request method
84 public $_httpversion = 'HTTP/1.0'; // default http request version
85 public $_submit_method = 'POST'; // default submit method
86 public $_submit_type = 'application/x-www-form-urlencoded'; // default submit type
87 public $_mime_boundary = ''; // MIME boundary for multipart/form-data submit type
88 public $_redirectaddr = false; // will be set if page fetched is a redirect
89 public $_redirectdepth = 0; // increments on an http redirect
90 public $_frameurls = []; // frame src urls
91 public $_framedepth = 0; // increments on frame depth
92
93 public $_isproxy = false; // set if using a proxy server
94 public $_fp_timeout = 30; // timeout for socket connection
95
96 /*======================================================================*\
97 Function: fetch
98 Purpose: fetch the contents of a web page
99 (and possibly other protocols in the
100 future like ftp, nntp, gopher, etc.)
101 Input: $URI the location of the page to fetch
102 Output: $this->results the output text from the fetch
103 \*======================================================================*/
104
105 public function fetch($URI)
106 {
107 $URI_PARTS = parse_url($URI);
108 if (!empty($URI_PARTS['user'])) {
109 $this->user = $URI_PARTS['user'];
110 }
111 if (!empty($URI_PARTS['pass'])) {
112 $this->pass = $URI_PARTS['pass'];
113 }
114 if (empty($URI_PARTS['query'])) {
115 $URI_PARTS['query'] = '';
116 }
117 if (empty($URI_PARTS['path'])) {
118 $URI_PARTS['path'] = '';
119 }
120
121 $fp = null;
122
123 switch (strtolower($URI_PARTS['scheme'])) {
124 case 'https':
125 if (!extension_loaded('openssl')) {
126 trigger_error('openssl extension required for HTTPS', E_USER_ERROR);
127 exit;
128 }
129 $this->port = 443;
130 break;
131 case 'http':
132 $this->scheme = strtolower($URI_PARTS['scheme']);
133 $this->host = $URI_PARTS['host'];
134 if (!empty($URI_PARTS['port'])) {
135 $this->port = $URI_PARTS['port'];
136 }
137 if ($this->_connect($fp)) {
138 if ($this->_isproxy) {
139 // using proxy, send entire URI
140 $this->_httprequest($URI, $fp, $URI, $this->_httpmethod);
141 } else {
142 $path = $URI_PARTS['path'] . ($URI_PARTS['query'] ? '?' . $URI_PARTS['query'] : '');
143 // no proxy, send only the path
144 $this->_httprequest($path, $fp, $URI, $this->_httpmethod);
145 }
146
147 $this->_disconnect($fp);
148
149 /* url was redirected, check if we've hit the max depth */
150 if ($this->_redirectaddr && $this->maxredirs > $this->_redirectdepth) {
151 // only follow redirect if it's on this site, or offsiteok is true
152 if (preg_match('|^https?://' . preg_quote($this->host) . '|i', $this->_redirectaddr) || $this->offsiteok) {
153 /* follow the redirect */
154 $this->_redirectdepth++;
155 $this->lastredirectaddr = $this->_redirectaddr;
156 $this->fetch($this->_redirectaddr);
157 }
158 }
159
160 if ($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) {
161 $frameurls = $this->_frameurls;
162 $this->_frameurls = [];
163
164
165 foreach ($frameurls as $frameurl) {
166
167 if ($this->_framedepth < $this->maxframes) {
168 $this->fetch($frameurl);
169 $this->_framedepth++;
170 } else {
171 break;
172 }
173 }
174 }
175 } else {
176 return false;
177 }
178 return $this;
179 break;
180 default:
181 // not a valid protocol
182 $this->error = 'Invalid protocol "' . $URI_PARTS['scheme'] . '"\n';
183 return false;
184 break;
185 }
186 return $this;
187 }
188
189 /*======================================================================*\
190 Function: submit
191 Purpose: submit an http(s) form
192 Input: $URI the location to post the data
193 $formvars the formvars to use.
194 format: $formvars["var"] = "val";
195 $formfiles an array of files to submit
196 format: $formfiles["var"] = "/dir/filename.ext";
197 Output: $this->results the text output from the post
198 \*======================================================================*/
199
200 public function submit($URI, $formvars = '', $formfiles = '')
201 {
202 unset($postdata);
203
204 $postdata = $this->_prepare_post_body($formvars, $formfiles);
205
206 $URI_PARTS = parse_url($URI);
207 if (!empty($URI_PARTS['user'])) {
208 $this->user = $URI_PARTS['user'];
209 }
210 if (!empty($URI_PARTS['pass'])) {
211 $this->pass = $URI_PARTS['pass'];
212 }
213 if (empty($URI_PARTS['query'])) {
214 $URI_PARTS['query'] = '';
215 }
216 if (empty($URI_PARTS['path'])) {
217 $URI_PARTS['path'] = '';
218 }
219
220 switch (strtolower($URI_PARTS['scheme'])) {
221 case 'https':
222 if (!extension_loaded('openssl')) {
223 trigger_error('openssl extension required for HTTPS', E_USER_ERROR);
224 exit;
225 }
226 $this->port = 443;
227 case 'http':
228 $this->scheme = strtolower($URI_PARTS['scheme']);
229 $this->host = $URI_PARTS['host'];
230 if (!empty($URI_PARTS['port'])) {
231 $this->port = $URI_PARTS['port'];
232 }
233 if ($this->_connect($fp)) {
234 if ($this->_isproxy) {
235 // using proxy, send entire URI
236 $this->_httprequest($URI, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata);
237 } else {
238 $path = $URI_PARTS['path'] . ($URI_PARTS['query'] ? '?' . $URI_PARTS['query'] : '');
239 // no proxy, send only the path
240 $this->_httprequest($path, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata);
241 }
242
243 $this->_disconnect($fp);
244
245 /* url was redirected, check if we've hit the max depth */
246 if ($this->_redirectaddr && $this->maxredirs > $this->_redirectdepth) {
247 if (!preg_match('|^' . $URI_PARTS['scheme'] . '://|', $this->_redirectaddr)) {
248 $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr, $URI_PARTS['scheme'] . '://' . $URI_PARTS['host']);
249 }
250
251 // only follow redirect if it's on this site, or offsiteok is true
252 if (preg_match('|^https?://' . preg_quote($this->host) . '|i', $this->_redirectaddr) || $this->offsiteok) {
253 /* follow the redirect */
254 $this->_redirectdepth++;
255 $this->lastredirectaddr = $this->_redirectaddr;
256 if (strpos($this->_redirectaddr, '?') > 0) {
257 $this->fetch($this->_redirectaddr);
258 } // the redirect has changed the request method from post to get
259 else {
260 $this->submit($this->_redirectaddr, $formvars, $formfiles);
261 }
262 }
263 }
264
265 if ($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) {
266 $frameurls = $this->_frameurls;
267 $this->_frameurls = [];
268
269 foreach ($frameurls as $frameurl) {
270 if ($this->_framedepth < $this->maxframes) {
271 $this->fetch($frameurl);
272 $this->_framedepth++;
273 } else {
274 break;
275 }
276 }
277 }
278 } else {
279 return false;
280 }
281 return $this;
282 break;
283 default:
284 // not a valid protocol
285 $this->error = 'Invalid protocol "' . $URI_PARTS['scheme'] . '"\n';
286 return false;
287 break;
288 }
289 return $this;
290 }
291
292 /*======================================================================*\
293 Function: fetchlinks
294 Purpose: fetch the links from a web page
295 Input: $URI where you are fetching from
296 Output: $this->results an array of the URLs
297 \*======================================================================*/
298
299 public function fetchlinks($URI)
300 {
301 if (false !== $this->fetch($URI)) {
302 if ($this->lastredirectaddr) {
303 $URI = $this->lastredirectaddr;
304 }
305 if (is_array($this->results)) {
306 foreach ($this->results as $x => $xValue) {
307 $this->results[$x] = $this->_striplinks($xValue);
308 }
309 } else {
310 $this->results = $this->_striplinks($this->results);
311 }
312
313 if ($this->expandlinks) {
314 $this->results = $this->_expandlinks($this->results, $URI);
315 }
316 return $this;
317 }
318
319 return false;
320 }
321
322 /*======================================================================*\
323 Function: fetchform
324 Purpose: fetch the form elements from a web page
325 Input: $URI where you are fetching from
326 Output: $this->results the resulting html form
327 \*======================================================================*/
328
329 public function fetchform($URI)
330 {
331 if (false !== $this->fetch($URI)) {
332 if (is_array($this->results)) {
333 foreach ($this->results as $x => $xValue) {
334 $this->results[$x] = $this->_stripform($xValue);
335 }
336 } else {
337 $this->results = $this->_stripform($this->results);
338 }
339
340 return $this;
341 }
342
343 return false;
344 }
345
346
347 /*======================================================================*\
348 Function: fetchtext
349 Purpose: fetch the text from a web page, stripping the links
350 Input: $URI where you are fetching from
351 Output: $this->results the text from the web page
352 \*======================================================================*/
353
354 public function fetchtext($URI)
355 {
356 if (false !== $this->fetch($URI)) {
357 if (is_array($this->results)) {
358 foreach ($this->results as $x => $xValue) {
359 $this->results[$x] = $this->_striptext($xValue);
360 }
361 } else {
362 $this->results = $this->_striptext($this->results);
363 }
364 return $this;
365 }
366
367 return false;
368 }
369
370 /*======================================================================*\
371 Function: submitlinks
372 Purpose: grab links from a form submission
373 Input: $URI where you are submitting from
374 Output: $this->results an array of the links from the post
375 \*======================================================================*/
376
377 public function submitlinks($URI, $formvars = '', $formfiles = '')
378 {
379 if (false !== $this->submit($URI, $formvars, $formfiles)) {
380 if ($this->lastredirectaddr) {
381 $URI = $this->lastredirectaddr;
382 }
383 if (is_array($this->results)) {
384 foreach ($this->results as $x => $xValue) {
385 $this->results[$x] = $this->_striplinks($xValue);
386 if ($this->expandlinks) {
387 $this->results[$x] = $this->_expandlinks($xValue, $URI);
388 }
389 }
390 } else {
391 $this->results = $this->_striplinks($this->results);
392 if ($this->expandlinks) {
393 $this->results = $this->_expandlinks($this->results, $URI);
394 }
395 }
396 return $this;
397 }
398
399 return false;
400 }
401
402 /*======================================================================*\
403 Function: submittext
404 Purpose: grab text from a form submission
405 Input: $URI where you are submitting from
406 Output: $this->results the text from the web page
407 \*======================================================================*/
408
409 public function submittext($URI, $formvars = '', $formfiles = '')
410 {
411 if (false !== $this->submit($URI, $formvars, $formfiles)) {
412 if ($this->lastredirectaddr) {
413 $URI = $this->lastredirectaddr;
414 }
415 if (is_array($this->results)) {
416 foreach ($this->results as $x => $xValue) {
417 $this->results[$x] = $this->_striptext($xValue);
418 if ($this->expandlinks) {
419 $this->results[$x] = $this->_expandlinks($xValue, $URI);
420 }
421 }
422 } else {
423 $this->results = $this->_striptext($this->results);
424 if ($this->expandlinks) {
425 $this->results = $this->_expandlinks($this->results, $URI);
426 }
427 }
428 return $this;
429 }
430
431 return false;
432 }
433
434
435 /*======================================================================*\
436 Function: set_submit_multipart
437 Purpose: Set the form submission content type to
438 multipart/form-data
439 \*======================================================================*/
440 public function set_submit_multipart()
441 {
442 $this->_submit_type = 'multipart/form-data';
443 return $this;
444 }
445
446
447 /*======================================================================*\
448 Function: set_submit_normal
449 Purpose: Set the form submission content type to
450 application/x-www-form-urlencoded
451 \*======================================================================*/
452 public function set_submit_normal()
453 {
454 $this->_submit_type = 'application/x-www-form-urlencoded';
455 return $this;
456 }
457
458
459
460
461 /*======================================================================*\
462 Private functions
463 \*======================================================================*/
464
465
466 /*======================================================================*\
467 Function: _striplinks
468 Purpose: strip the hyperlinks from an html document
469 Input: $document document to strip.
470 Output: $match an array of the links
471 \*======================================================================*/
472
473 public function _striplinks($document)
474 {
475 preg_match_all("'<\s*a\s.*?href\s*=\s* # find <a href=
476 ([\"\'])? # find single or double quote
477 (?(1) (.*?)\\1 | ([^\s>]+)) # if quote found, match up to next matching
478 # quote, otherwise match up to next space
479 'isx", $document, $links);
480
481
482 // catenate the non-empty matches from the conditional subpattern
483
484 foreach ($links[2] as $val) {
485 if (!empty($val)) {
486 $match[] = $val;
487 }
488 }
489
490 foreach ($links[3] as $val) {
491 if (!empty($val)) {
492 $match[] = $val;
493 }
494 }
495
496 // return the links
497 return $match;
498 }
499
500 /*======================================================================*\
501 Function: _stripform
502 Purpose: strip the form elements from an html document
503 Input: $document document to strip.
504 Output: $match an array of the links
505 \*======================================================================*/
506
507 public function _stripform($document)
508 {
509 preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))'Usi", $document, $elements);
510
511 // catenate the matches
512 // return the links
513 return implode("\r\n", $elements[0]);
514 }
515
516
517 /*======================================================================*\
518 Function: _striptext
519 Purpose: strip the text from an html document
520 Input: $document document to strip.
521 Output: $text the resulting text
522 \*======================================================================*/
523
524 public function _striptext($document)
525 {
526
527 // I didn't use preg eval (//e) since that is only available in PHP 4.0.
528 // so, list your entities one by one here. I included some of the
529 // more common ones.
530
531 $search = [
532 "'<script[^>]*?>.*?</script>'si", // strip out javascript
533 "'<[\/\!]*?[^<>]*?>'si", // strip out html tags
534 "'([\r\n])[\s]+'", // strip out white space
535 "'&(quot|#34|#034|#x22);'i", // replace html entities
536 "'&(amp|#38|#038|#x26);'i", // added hexadecimal values
537 "'&(lt|#60|#060|#x3c);'i",
538 "'&(gt|#62|#062|#x3e);'i",
539 "'&(nbsp|#160|#xa0);'i",
540 "'&(iexcl|#161);'i",
541 "'&(cent|#162);'i",
542 "'&(pound|#163);'i",
543 "'&(copy|#169);'i",
544 "'&(reg|#174);'i",
545 "'&(deg|#176);'i",
546 "'&(#39|#039|#x27);'",
547 "'&(euro|#8364);'i", // europe
548 "'&a(uml|UML);'", // german
549 "'&o(uml|UML);'",
550 "'&u(uml|UML);'",
551 "'&A(uml|UML);'",
552 "'&O(uml|UML);'",
553 "'&U(uml|UML);'",
554 "'&szlig;'i",
555 ];
556 $replace = [
557 '',
558 '',
559 "\\1",
560 '"',
561 '&',
562 '<',
563 '>',
564 ' ',
565 chr(161),
566 chr(162),
567 chr(163),
568 chr(169),
569 chr(174),
570 chr(176),
571 chr(39),
572 chr(128),
573 /*
574 * use CHR code for UTF-8
575 * Marijuana
576 */
577 chr(228),
578 chr(246),
579 chr(252),
580 chr(196),
581 chr(214),
582 chr(220),
583 chr(223),
584 ];
585
586 return preg_replace($search, $replace, $document);
587 }
588
589 /*======================================================================*\
590 Function: _expandlinks
591 Purpose: expand each link into a fully qualified URL
592 Input: $links the links to qualify
593 $URI the full URI to get the base from
594 Output: $expandedLinks the expanded links
595 \*======================================================================*/
596
597 public function _expandlinks($links, $URI)
598 {
599 preg_match("/^[^\?]+/", $URI, $match);
600
601 $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|", '', $match[0]);
602 $match = preg_replace('|/$|', '', $match);
603 $match_part = parse_url($match);
604 $match_root =
605 $match_part['scheme'] . '://' . $match_part['host'];
606
607 $search = [
608 '|^https://' . preg_quote($this->host) . '|i',
609 "|^(\/)|i",
610 '|^(?!https://)(?!mailto:)|i',
611 "|/\./|",
612 "|/[^\/]+/\.\./|"
613 ];
614
615 $replace = [
616 '',
617 $match_root . '/',
618 $match . '/',
619 '/',
620 '/'
621 ];
622
623 return preg_replace($search, $replace, $links);
624 }
625
626 /*======================================================================*\
627 Function: _httprequest
628 Purpose: go get the http(s) data from the server
629 Input: $url the url to fetch
630 $fp the current open file pointer
631 $URI the full URI
632 $body body contents to send if any (POST)
633 Output:
634 \*======================================================================*/
635
636 public function _httprequest($url, $fp, $URI, $http_method, $content_type = '', $body = '')
637 {
638 $cookie_headers = '';
639 if ($this->passcookies && $this->_redirectaddr) {
640 $this->setcookies();
641 }
642
643 $URI_PARTS = parse_url($URI);
644 if (empty($url)) {
645 $url = '/';
646 }
647 $headers = $http_method . ' ' . $url . ' ' . $this->_httpversion . "\r\n";
648 if (!empty($this->host) && !isset($this->rawheaders['Host'])) {
649 $headers .= 'Host: ' . $this->host;
650 if (!empty($this->port) && '80' !== $this->port) {
651 $headers .= ':' . $this->port;
652 }
653 $headers .= "\r\n";
654 }
655 if (!empty($this->agent)) {
656 $headers .= 'User-Agent: ' . $this->agent . "\r\n";
657 }
658 if (!empty($this->accept)) {
659 $headers .= 'Accept: ' . $this->accept . "\r\n";
660 }
661 if ($this->use_gzip) {
662 // make sure PHP was built with --with-zlib
663 // and we can handle gzipp'ed data
664 if (function_exists('gzinflate')) {
665 $headers .= "Accept-encoding: gzip\r\n";
666 } else {
667 trigger_error(
668 'use_gzip is on, but PHP was built without zlib support.' . ' Requesting file(s) without gzip encoding.',
669 E_USER_NOTICE);
670 }
671 }
672 if (!empty($this->referer)) {
673 $headers .= 'Referer: ' . $this->referer . "\r\n";
674 }
675 if (!empty($this->cookies)) {
676 if (!is_array($this->cookies)) {
677 $this->cookies = (array)$this->cookies;
678 }
679
680 reset($this->cookies);
681 if (count($this->cookies) > 0) {
682 $cookie_headers .= 'Cookie: ';
683 foreach ($this->cookies as $cookieKey => $cookieVal) {
684 $cookie_headers .= $cookieKey . '=' . urlencode($cookieVal) . '; ';
685 }
686 $headers .= substr($cookie_headers, 0, -2) . "\r\n";
687 }
688 }
689 if (!empty($this->rawheaders)) {
690 if (!is_array($this->rawheaders)) {
691 $this->rawheaders = (array)$this->rawheaders;
692 }
693 foreach ($this->rawheaders as $headerKey => $headerVal) {
694 $headers .= $headerKey . ': ' . $headerVal . "\r\n";
695 }
696 }
697 if (!empty($content_type)) {
698 $headers .= "Content-type: $content_type";
699 if ('multipart/form-data' === $content_type) {
700 $headers .= '; boundary=' . $this->_mime_boundary;
701 }
702 $headers .= "\r\n";
703 }
704 if (!empty($body)) {
705 $headers .= 'Content-length: ' . strlen($body) . "\r\n";
706 }
707 if (!empty($this->user) || !empty($this->pass)) {
708 $headers .= 'Authorization: Basic ' . base64_encode($this->user . ':' . $this->pass) . "\r\n";
709 }
710
711 //add proxy auth headers
712 if (!empty($this->proxy_user)) {
713 $headers .= 'Proxy-Authorization: ' . 'Basic ' . base64_encode($this->proxy_user . ':' . $this->proxy_pass) . "\r\n";
714 }
715
716
717 $headers .= "\r\n";
718
719 // set the read timeout if needed
720 if ($this->read_timeout > 0) {
721 stream_set_timeout($fp, $this->read_timeout);
722 }
723 $this->timed_out = false;
724
725 fwrite($fp, $headers . $body, strlen($headers . $body));
726
727 $this->_redirectaddr = false;
728 unset($this->headers);
729
730 // content was returned gzip encoded?
731 $is_gzipped = false;
732
733 while ($currentHeader = fgets($fp, $this->_maxlinelen)) {
734 if ($this->read_timeout > 0 && $this->_check_timeout($fp)) {
735 $this->status = -100;
736 return false;
737 }
738
739 if ("\r\n" == $currentHeader) {
740 break;
741 }
742
743 // if a header begins with Location: or URI:, set the redirect
744 if (preg_match('/^(Location:|URI:)/i', $currentHeader)) {
745 // get URL portion of the redirect
746 preg_match('/^(Location:|URI:)[ ]+(.*)/i', rtrim($currentHeader), $matches);
747 // look for :// in the Location header to see if hostname is included
748 if (!preg_match("|\:\/\/|", $matches[2])) {
749 // no host in the path, so prepend
750 $this->_redirectaddr = $URI_PARTS['scheme'] . '://' . $this->host . ':' . $this->port;
751 // eliminate double slash
752 if (!preg_match('|^/|', $matches[2])) {
753 $this->_redirectaddr .= '/' . $matches[2];
754 } else {
755 $this->_redirectaddr .= $matches[2];
756 }
757 } else {
758 $this->_redirectaddr = $matches[2];
759 }
760 }
761
762 if (preg_match('|^HTTP/|', $currentHeader)) {
763 if (preg_match("|^HTTP/[^\s]*\s(.*?)\s|", $currentHeader, $status)) {
764 $this->status = $status[1];
765 }
766 $this->response_code = $currentHeader;
767 }
768
770 // The check for gzip-encoded content in responses should be case-insensitive, per RFC 2616.
771 // Patch by Evan Anderson
772 // - if (preg_match('/Content-Encoding: gzip/', $currentHeader)) {
773
774 if (preg_match("/Content-Encoding: gzip/i", $currentHeader)) {
775 $is_gzipped = true;
776 }
777
778 $this->headers[] = $currentHeader;
779 }
780
781 $results = '';
782 do {
783 $_data = fread($fp, $this->maxlength);
784 if ($_data == '') {
785 break;
786 }
787 $results .= $_data;
788 } while (true);
789
790 // gunzip
791 if ($is_gzipped) {
792 // per https://www.php.net/manual/en/function.gzencode.php
793 $results = substr($results, 10);
794 $results = gzinflate($results);
795 }
796
797 if ($this->read_timeout > 0 && $this->_check_timeout($fp)) {
798 $this->status = -100;
799 return false;
800 }
801
802 // check if there is a a redirect meta tag
803
804 if (preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i", $results, $match)) {
805 $this->_redirectaddr = $this->_expandlinks($match[1], $URI);
806 }
807
808 // have we hit our frame depth and is there frame src to fetch?
809 if (($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\">]+)'i", $results, $match)) {
810 $this->results[] = $results;
811 foreach ($match[1] as $xValue) {
812 $this->_frameurls[] = $this->_expandlinks($xValue, $URI_PARTS['scheme'] . '://' . $this->host);
813 }
814 } // have we already fetched framed content?
815 elseif (is_array($this->results)) {
816 $this->results[] = $results;
817 }
818 // no framed content
819 else {
820 $this->results = $results;
821 }
822
823 return $this;
824 }
825
826 /*======================================================================*\
827 Function: setcookies()
828 Purpose: set cookies for a redirection
829 \*======================================================================*/
830
831 public function setcookies()
832 {
833 foreach ($this->headers as $x => $xValue) {
834 if (preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $this->headers[$x], $match)) {
835 $this->cookies[$match[1]] = urldecode($match[2]);
836 }
837 }
838 return $this;
839 }
840
841
842 /*======================================================================*\
843 Function: _check_timeout
844 Purpose: checks whether timeout has occurred
845 Input: $fp file pointer
846 \*======================================================================*/
847
848 public function _check_timeout($fp)
849 {
850 if ($this->read_timeout > 0) {
851 $fp_status = stream_get_meta_data($fp);
852 if ($fp_status['timed_out']) {
853 $this->timed_out = true;
854 return true;
855 }
856 }
857 return false;
858 }
859
860 /*======================================================================*\
861 Function: _connect
862 Purpose: make a socket connection
863 Input: $fp file pointer
864 \*======================================================================*/
865
866 public function _connect(&$fp)
867 {
868 if (!empty($this->proxy_host) && !empty($this->proxy_port)) {
869 $this->_isproxy = true;
870
871 $host = $this->proxy_host;
872 $port = $this->proxy_port;
873
874 if ('https' == $this->scheme) {
875 trigger_error('HTTPS connections over proxy are currently not supported', E_USER_ERROR);
876 exit;
877 }
878 } else {
879 $host = $this->host;
880 $port = $this->port;
881 }
882
883 $this->status = 0;
884
885 $context_opts = [];
886
887 if ('https' == $this->scheme) {
888 // if cafile or capath is specified, enable certificate
889 // verification (including name checks)
890 if (isset($this->cafile) || isset($this->capath)) {
891 $context_opts['ssl'] = [
892 'verify_peer' => true,
893 'CN_match' => $this->host,
894 'disable_compression' => true,
895 ];
896
897 if (isset($this->cafile)) {
898 $context_opts['ssl']['cafile'] = $this->cafile;
899 }
900 if (isset($this->capath)) {
901 $context_opts['ssl']['capath'] = $this->capath;
902 }
903 }
904
905 $host = 'ssl://' . $host;
906 }
907
908 $context = stream_context_create($context_opts);
909
910 if (PHP_VERSION_ID > 50000) {
911 if($this->scheme == 'http'){
912 $host = "tcp://" . $host;
913 }
914
915// if ('http' == $this->scheme) {
916// $host = 'tcp://' . $host;
917// }
918 $fp = stream_socket_client(
919 "$host:$port",
920 $errno,
921 $errmsg,
922 $this->_fp_timeout,
923 STREAM_CLIENT_CONNECT,
924 $context);
925 } else {
927 // TODO - Test
928 $fp = fsockopen(
929 $host,
930 $port,
931 $errno,
932 $errstr,
933 $this->_fp_timeout,
934 $context);
935 }
936
937 if ($fp) {
938 // socket connection succeeded
939 return true;
940 }
941
942// socket connection failed
943 $this->status = $errno;
944 switch ($errno) {
945 case -3:
946 $this->error = 'socket creation failed (-3)';
947 break;
948 case -4:
949 $this->error = 'dns lookup failure (-4)';
950 break;
951 case -5:
952 $this->error = 'connection refused or timed out (-5)';
953 break;
954 default:
955 $this->error = 'connection failed (' . $errno . ')';
956 }
957 return false;
958 }
959
960 /*======================================================================*\
961 Function: _disconnect
962 Purpose: disconnect a socket connection
963 Input: $fp file pointer
964 \*======================================================================*/
965
966 public function _disconnect($fp)
967 {
968 return (fclose($fp));
969 }
970
971
972 /*======================================================================*\
973 Function: _prepare_post_body
974 Purpose: Prepare post body according to encoding type
975 Input: $formvars - form variables
976 $formfiles - form upload files
977 Output: post body
978 \*======================================================================*/
979
980 public function _prepare_post_body($formvars, $formfiles)
981 {
982 $formvars = (array)$formvars;
983 $formfiles = (array)$formfiles;
984 $postdata = '';
985
986 if (0 == count($formvars) && 0 == count($formfiles)) {
987 return;
988 }
989
990 switch ($this->_submit_type) {
991 case 'application/x-www-form-urlencoded':
992 reset($formvars);
993 foreach ($formvars as $key => $val) {
994 if (is_array($val) || is_object($val)) {
995 foreach ($val as $cur_key => $cur_val) {
996 $postdata .= urlencode($key) ."[$cur_key]=". urlencode($cur_val)."&";
998 }
999 } else {
1000 $postdata .= urlencode($key) . '=' . urlencode($val) . '&';
1001 }
1002 }
1003 $postdata = substr($postdata, 0, strlen($postdata) - 1);
1004 break;
1005
1006 case 'multipart/form-data':
1007 $this->_mime_boundary = 'Snoopy' . md5(uniqid(microtime(), true));
1008
1009 reset($formvars);
1010 foreach ($formvars as $key => $val) {
1011 if (is_array($val) || is_object($val)) {
1012 foreach ($val as $cur_val) {
1013 $postdata .= '--' . $this->_mime_boundary . "\r\n";
1014 $postdata .= "Content-Disposition: form-data; name=\"$key\[\]\"\r\n\r\n";
1015 $postdata .= "$cur_val\r\n";
1016 }
1017 } else {
1018 $postdata .= '--' . $this->_mime_boundary . "\r\n";
1019 $postdata .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n";
1020 $postdata .= "$val\r\n";
1021 }
1022 }
1023
1024 reset($formfiles);
1025 foreach ($formfiles as $field_name => $file_names) {
1026 $file_names = (array)$file_names;
1027 foreach ($file_names as $file_name) {
1028 if (!is_readable($file_name)) {
1029 continue;
1030 }
1031
1032 $fp = fopen($file_name, 'r');
1033 $file_content = fread($fp, filesize($file_name));
1034 fclose($fp);
1035 $base_name = basename($file_name);
1036
1037 $postdata .= '--' . $this->_mime_boundary . "\r\n";
1038 $postdata .= "Content-Disposition: form-data; name=\"$field_name\"; filename=\"$base_name\"\r\n\r\n";
1039 $postdata .= "$file_content\r\n";
1040 }
1041 }
1042 $postdata .= '--' . $this->_mime_boundary . "--\r\n";
1043 break;
1044 // XOOPS2 Hack begin
1045 // Added on March 4, 2003 by onokazu@xoops.org
1046 case 'text/xml':
1047 default:
1048 $postdata = $formvars[0];
1049 break;
1050 // XOOPS2 Hack end
1051 }
1052
1053 return $postdata;
1054 }
1055
1056 /*======================================================================*\
1057 Function: getResults
1058 Purpose: return the results of a request
1059 Output: string results
1060 \*======================================================================*/
1061
1062 public function getResults()
1063 {
1064 return $this->results;
1065 }
1066}
_connect(&$fp)
Definition snoopy.php:866
_httprequest($url, $fp, $URI, $http_method, $content_type='', $body='')
Definition snoopy.php:636
_prepare_post_body($formvars, $formfiles)
Definition snoopy.php:980