340 * certificate file as a string. (Using true uses the system-wide root |
340 * certificate file as a string. (Using true uses the system-wide root |
341 * certificate store instead, but this may have different behaviour |
341 * certificate store instead, but this may have different behaviour |
342 * across transports.) |
342 * across transports.) |
343 * (string|boolean, default: library/Requests/Transport/cacert.pem) |
343 * (string|boolean, default: library/Requests/Transport/cacert.pem) |
344 * - `verifyname`: Should we verify the common name in the SSL certificate? |
344 * - `verifyname`: Should we verify the common name in the SSL certificate? |
345 * (boolean: default, true) |
345 * (boolean, default: true) |
346 * - `data_format`: How should we send the `$data` parameter? |
346 * - `data_format`: How should we send the `$data` parameter? |
347 * (string, one of 'query' or 'body', default: 'query' for |
347 * (string, one of 'query' or 'body', default: 'query' for |
348 * HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH) |
348 * HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH) |
349 * |
349 * |
350 * @throws Requests_Exception On invalid URLs (`nonhttp`) |
350 * @throws Requests_Exception On invalid URLs (`nonhttp`) |
372 if (is_string($options['transport'])) { |
372 if (is_string($options['transport'])) { |
373 $transport = new $transport(); |
373 $transport = new $transport(); |
374 } |
374 } |
375 } |
375 } |
376 else { |
376 else { |
377 $need_ssl = (0 === stripos($url, 'https://')); |
377 $need_ssl = (stripos($url, 'https://') === 0); |
378 $capabilities = array('ssl' => $need_ssl); |
378 $capabilities = array('ssl' => $need_ssl); |
379 $transport = self::get_transport($capabilities); |
379 $transport = self::get_transport($capabilities); |
380 } |
380 } |
381 $response = $transport->request($url, $headers, $data, $options); |
381 $response = $transport->request($url, $headers, $data, $options); |
382 |
382 |
383 $options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options)); |
383 $options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options)); |
384 |
384 |
501 * @param boolean $multirequest Is this a multirequest? |
501 * @param boolean $multirequest Is this a multirequest? |
502 * @return array Default option values |
502 * @return array Default option values |
503 */ |
503 */ |
504 protected static function get_default_options($multirequest = false) { |
504 protected static function get_default_options($multirequest = false) { |
505 $defaults = array( |
505 $defaults = array( |
506 'timeout' => 10, |
506 'timeout' => 10, |
507 'connect_timeout' => 10, |
507 'connect_timeout' => 10, |
508 'useragent' => 'php-requests/' . self::VERSION, |
508 'useragent' => 'php-requests/' . self::VERSION, |
509 'protocol_version' => 1.1, |
509 'protocol_version' => 1.1, |
510 'redirected' => 0, |
510 'redirected' => 0, |
511 'redirects' => 10, |
511 'redirects' => 10, |
512 'follow_redirects' => true, |
512 'follow_redirects' => true, |
513 'blocking' => true, |
513 'blocking' => true, |
514 'type' => self::GET, |
514 'type' => self::GET, |
515 'filename' => false, |
515 'filename' => false, |
516 'auth' => false, |
516 'auth' => false, |
517 'proxy' => false, |
517 'proxy' => false, |
518 'cookies' => false, |
518 'cookies' => false, |
519 'max_bytes' => false, |
519 'max_bytes' => false, |
520 'idn' => true, |
520 'idn' => true, |
521 'hooks' => null, |
521 'hooks' => null, |
522 'transport' => null, |
522 'transport' => null, |
523 'verify' => Requests::get_certificate_path(), |
523 'verify' => self::get_certificate_path(), |
524 'verifyname' => true, |
524 'verifyname' => true, |
525 ); |
525 ); |
526 if ($multirequest !== false) { |
526 if ($multirequest !== false) { |
527 $defaults['complete'] = null; |
527 $defaults['complete'] = null; |
528 } |
528 } |
529 return $defaults; |
529 return $defaults; |
533 * Get default certificate path. |
533 * Get default certificate path. |
534 * |
534 * |
535 * @return string Default certificate path. |
535 * @return string Default certificate path. |
536 */ |
536 */ |
537 public static function get_certificate_path() { |
537 public static function get_certificate_path() { |
538 if ( ! empty( Requests::$certificate_path ) ) { |
538 if (!empty(self::$certificate_path)) { |
539 return Requests::$certificate_path; |
539 return self::$certificate_path; |
540 } |
540 } |
541 |
541 |
542 return dirname(__FILE__) . '/Requests/Transport/cacert.pem'; |
542 return dirname(__FILE__) . '/Requests/Transport/cacert.pem'; |
543 } |
543 } |
544 |
544 |
545 /** |
545 /** |
546 * Set default certificate path. |
546 * Set default certificate path. |
547 * |
547 * |
548 * @param string $path Certificate path, pointing to a PEM file. |
548 * @param string $path Certificate path, pointing to a PEM file. |
549 */ |
549 */ |
550 public static function set_certificate_path( $path ) { |
550 public static function set_certificate_path($path) { |
551 Requests::$certificate_path = $path; |
551 self::$certificate_path = $path; |
552 } |
552 } |
553 |
553 |
554 /** |
554 /** |
555 * Set the default values |
555 * Set the default values |
556 * |
556 * |
593 if ($options['cookies'] !== false) { |
593 if ($options['cookies'] !== false) { |
594 $options['cookies']->register($options['hooks']); |
594 $options['cookies']->register($options['hooks']); |
595 } |
595 } |
596 |
596 |
597 if ($options['idn'] !== false) { |
597 if ($options['idn'] !== false) { |
598 $iri = new Requests_IRI($url); |
598 $iri = new Requests_IRI($url); |
599 $iri->host = Requests_IDNAEncoder::encode($iri->ihost); |
599 $iri->host = Requests_IDNAEncoder::encode($iri->ihost); |
600 $url = $iri->uri; |
600 $url = $iri->uri; |
601 } |
601 } |
602 |
602 |
603 // Massage the type to ensure we support it. |
603 // Massage the type to ensure we support it. |
604 $type = strtoupper($type); |
604 $type = strtoupper($type); |
605 |
605 |
606 if (!isset($options['data_format'])) { |
606 if (!isset($options['data_format'])) { |
607 if (in_array($type, array(self::HEAD, self::GET, self::DELETE))) { |
607 if (in_array($type, array(self::HEAD, self::GET, self::DELETE), true)) { |
608 $options['data_format'] = 'query'; |
608 $options['data_format'] = 'query'; |
609 } |
609 } |
610 else { |
610 else { |
611 $options['data_format'] = 'body'; |
611 $options['data_format'] = 'body'; |
612 } |
612 } |
631 $return = new Requests_Response(); |
631 $return = new Requests_Response(); |
632 if (!$options['blocking']) { |
632 if (!$options['blocking']) { |
633 return $return; |
633 return $return; |
634 } |
634 } |
635 |
635 |
636 $return->raw = $headers; |
636 $return->raw = $headers; |
637 $return->url = $url; |
637 $return->url = (string) $url; |
|
638 $return->body = ''; |
638 |
639 |
639 if (!$options['filename']) { |
640 if (!$options['filename']) { |
640 if (($pos = strpos($headers, "\r\n\r\n")) === false) { |
641 $pos = strpos($headers, "\r\n\r\n"); |
|
642 if ($pos === false) { |
641 // Crap! |
643 // Crap! |
642 throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator'); |
644 throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator'); |
643 } |
645 } |
644 |
646 |
645 $headers = substr($return->raw, 0, $pos); |
647 $headers = substr($return->raw, 0, $pos); |
646 $return->body = substr($return->raw, $pos + strlen("\n\r\n\r")); |
648 // Headers will always be separated from the body by two new lines - `\n\r\n\r`. |
647 } |
649 $body = substr($return->raw, $pos + 4); |
648 else { |
650 if (!empty($body)) { |
649 $return->body = ''; |
651 $return->body = $body; |
|
652 } |
650 } |
653 } |
651 // Pretend CRLF = LF for compatibility (RFC 2616, section 19.3) |
654 // Pretend CRLF = LF for compatibility (RFC 2616, section 19.3) |
652 $headers = str_replace("\r\n", "\n", $headers); |
655 $headers = str_replace("\r\n", "\n", $headers); |
653 // Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2) |
656 // Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2) |
654 $headers = preg_replace('/\n[ \t]/', ' ', $headers); |
657 $headers = preg_replace('/\n[ \t]/', ' ', $headers); |
656 preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches); |
659 preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches); |
657 if (empty($matches)) { |
660 if (empty($matches)) { |
658 throw new Requests_Exception('Response could not be parsed', 'noversion', $headers); |
661 throw new Requests_Exception('Response could not be parsed', 'noversion', $headers); |
659 } |
662 } |
660 $return->protocol_version = (float) $matches[1]; |
663 $return->protocol_version = (float) $matches[1]; |
661 $return->status_code = (int) $matches[2]; |
664 $return->status_code = (int) $matches[2]; |
662 if ($return->status_code >= 200 && $return->status_code < 300) { |
665 if ($return->status_code >= 200 && $return->status_code < 300) { |
663 $return->success = true; |
666 $return->success = true; |
664 } |
667 } |
665 |
668 |
666 foreach ($headers as $header) { |
669 foreach ($headers as $header) { |
667 list($key, $value) = explode(':', $header, 2); |
670 list($key, $value) = explode(':', $header, 2); |
668 $value = trim($value); |
671 $value = trim($value); |
669 preg_replace('#(\s+)#i', ' ', $value); |
672 preg_replace('#(\s+)#i', ' ', $value); |
670 $return->headers[$key] = $value; |
673 $return->headers[$key] = $value; |
671 } |
674 } |
672 if (isset($return->headers['transfer-encoding'])) { |
675 if (isset($return->headers['transfer-encoding'])) { |
673 $return->body = self::decode_chunked($return->body); |
676 $return->body = self::decode_chunked($return->body); |
730 * @param array $request Request data as passed into {@see Requests::request_multiple()} |
733 * @param array $request Request data as passed into {@see Requests::request_multiple()} |
731 * @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object |
734 * @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object |
732 */ |
735 */ |
733 public static function parse_multiple(&$response, $request) { |
736 public static function parse_multiple(&$response, $request) { |
734 try { |
737 try { |
735 $url = $request['url']; |
738 $url = $request['url']; |
736 $headers = $request['headers']; |
739 $headers = $request['headers']; |
737 $data = $request['data']; |
740 $data = $request['data']; |
738 $options = $request['options']; |
741 $options = $request['options']; |
739 $response = self::parse_response($response, $url, $headers, $data, $options); |
742 $response = self::parse_response($response, $url, $headers, $data, $options); |
740 } |
743 } |
741 catch (Requests_Exception $e) { |
744 catch (Requests_Exception $e) { |
742 $response = $e; |
745 $response = $e; |
743 } |
746 } |
826 if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") { |
827 if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") { |
827 // Not actually compressed. Probably cURL ruining this for us. |
828 // Not actually compressed. Probably cURL ruining this for us. |
828 return $data; |
829 return $data; |
829 } |
830 } |
830 |
831 |
831 if (function_exists('gzdecode') && ($decoded = @gzdecode($data)) !== false) { |
832 if (function_exists('gzdecode')) { |
|
833 // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.gzdecodeFound -- Wrapped in function_exists() for PHP 5.2. |
|
834 $decoded = @gzdecode($data); |
|
835 if ($decoded !== false) { |
|
836 return $decoded; |
|
837 } |
|
838 } |
|
839 |
|
840 if (function_exists('gzinflate')) { |
|
841 $decoded = @gzinflate($data); |
|
842 if ($decoded !== false) { |
|
843 return $decoded; |
|
844 } |
|
845 } |
|
846 |
|
847 $decoded = self::compatible_gzinflate($data); |
|
848 if ($decoded !== false) { |
832 return $decoded; |
849 return $decoded; |
833 } |
850 } |
834 elseif (function_exists('gzinflate') && ($decoded = @gzinflate($data)) !== false) { |
851 |
835 return $decoded; |
852 if (function_exists('gzuncompress')) { |
836 } |
853 $decoded = @gzuncompress($data); |
837 elseif (($decoded = self::compatible_gzinflate($data)) !== false) { |
854 if ($decoded !== false) { |
838 return $decoded; |
855 return $decoded; |
839 } |
856 } |
840 elseif (function_exists('gzuncompress') && ($decoded = @gzuncompress($data)) !== false) { |
|
841 return $decoded; |
|
842 } |
857 } |
843 |
858 |
844 return $data; |
859 return $data; |
845 } |
860 } |
846 |
861 |
859 * @since 2.8.1 |
874 * @since 2.8.1 |
860 * @link https://core.trac.wordpress.org/ticket/18273 |
875 * @link https://core.trac.wordpress.org/ticket/18273 |
861 * @link https://secure.php.net/manual/en/function.gzinflate.php#70875 |
876 * @link https://secure.php.net/manual/en/function.gzinflate.php#70875 |
862 * @link https://secure.php.net/manual/en/function.gzinflate.php#77336 |
877 * @link https://secure.php.net/manual/en/function.gzinflate.php#77336 |
863 * |
878 * |
864 * @param string $gzData String to decompress. |
879 * @param string $gz_data String to decompress. |
865 * @return string|bool False on failure. |
880 * @return string|bool False on failure. |
866 */ |
881 */ |
867 public static function compatible_gzinflate($gzData) { |
882 public static function compatible_gzinflate($gz_data) { |
868 // Compressed data might contain a full zlib header, if so strip it for |
883 // Compressed data might contain a full zlib header, if so strip it for |
869 // gzinflate() |
884 // gzinflate() |
870 if (substr($gzData, 0, 3) == "\x1f\x8b\x08") { |
885 if (substr($gz_data, 0, 3) === "\x1f\x8b\x08") { |
871 $i = 10; |
886 $i = 10; |
872 $flg = ord(substr($gzData, 3, 1)); |
887 $flg = ord(substr($gz_data, 3, 1)); |
873 if ($flg > 0) { |
888 if ($flg > 0) { |
874 if ($flg & 4) { |
889 if ($flg & 4) { |
875 list($xlen) = unpack('v', substr($gzData, $i, 2)); |
890 list($xlen) = unpack('v', substr($gz_data, $i, 2)); |
876 $i = $i + 2 + $xlen; |
891 $i += 2 + $xlen; |
877 } |
892 } |
878 if ($flg & 8) { |
893 if ($flg & 8) { |
879 $i = strpos($gzData, "\0", $i) + 1; |
894 $i = strpos($gz_data, "\0", $i) + 1; |
880 } |
895 } |
881 if ($flg & 16) { |
896 if ($flg & 16) { |
882 $i = strpos($gzData, "\0", $i) + 1; |
897 $i = strpos($gz_data, "\0", $i) + 1; |
883 } |
898 } |
884 if ($flg & 2) { |
899 if ($flg & 2) { |
885 $i = $i + 2; |
900 $i += 2; |
886 } |
901 } |
887 } |
902 } |
888 $decompressed = self::compatible_gzinflate(substr($gzData, $i)); |
903 $decompressed = self::compatible_gzinflate(substr($gz_data, $i)); |
889 if (false !== $decompressed) { |
904 if ($decompressed !== false) { |
890 return $decompressed; |
905 return $decompressed; |
891 } |
906 } |
892 } |
907 } |
893 |
908 |
894 // If the data is Huffman Encoded, we must first strip the leading 2 |
909 // If the data is Huffman Encoded, we must first strip the leading 2 |
900 // See https://decompres.blogspot.com/ for a quick explanation of this |
915 // See https://decompres.blogspot.com/ for a quick explanation of this |
901 // data type |
916 // data type |
902 $huffman_encoded = false; |
917 $huffman_encoded = false; |
903 |
918 |
904 // low nibble of first byte should be 0x08 |
919 // low nibble of first byte should be 0x08 |
905 list(, $first_nibble) = unpack('h', $gzData); |
920 list(, $first_nibble) = unpack('h', $gz_data); |
906 |
921 |
907 // First 2 bytes should be divisible by 0x1F |
922 // First 2 bytes should be divisible by 0x1F |
908 list(, $first_two_bytes) = unpack('n', $gzData); |
923 list(, $first_two_bytes) = unpack('n', $gz_data); |
909 |
924 |
910 if (0x08 == $first_nibble && 0 == ($first_two_bytes % 0x1F)) { |
925 if ($first_nibble === 0x08 && ($first_two_bytes % 0x1F) === 0) { |
911 $huffman_encoded = true; |
926 $huffman_encoded = true; |
912 } |
927 } |
913 |
928 |
914 if ($huffman_encoded) { |
929 if ($huffman_encoded) { |
915 if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) { |
930 $decompressed = @gzinflate(substr($gz_data, 2)); |
|
931 if ($decompressed !== false) { |
916 return $decompressed; |
932 return $decompressed; |
917 } |
933 } |
918 } |
934 } |
919 |
935 |
920 if ("\x50\x4b\x03\x04" == substr($gzData, 0, 4)) { |
936 if (substr($gz_data, 0, 4) === "\x50\x4b\x03\x04") { |
921 // ZIP file format header |
937 // ZIP file format header |
922 // Offset 6: 2 bytes, General-purpose field |
938 // Offset 6: 2 bytes, General-purpose field |
923 // Offset 26: 2 bytes, filename length |
939 // Offset 26: 2 bytes, filename length |
924 // Offset 28: 2 bytes, optional field length |
940 // Offset 28: 2 bytes, optional field length |
925 // Offset 30: Filename field, followed by optional field, followed |
941 // Offset 30: Filename field, followed by optional field, followed |
926 // immediately by data |
942 // immediately by data |
927 list(, $general_purpose_flag) = unpack('v', substr($gzData, 6, 2)); |
943 list(, $general_purpose_flag) = unpack('v', substr($gz_data, 6, 2)); |
928 |
944 |
929 // If the file has been compressed on the fly, 0x08 bit is set of |
945 // If the file has been compressed on the fly, 0x08 bit is set of |
930 // the general purpose field. We can use this to differentiate |
946 // the general purpose field. We can use this to differentiate |
931 // between a compressed document, and a ZIP file |
947 // between a compressed document, and a ZIP file |
932 $zip_compressed_on_the_fly = (0x08 == (0x08 & $general_purpose_flag)); |
948 $zip_compressed_on_the_fly = ((0x08 & $general_purpose_flag) === 0x08); |
933 |
949 |
934 if (!$zip_compressed_on_the_fly) { |
950 if (!$zip_compressed_on_the_fly) { |
935 // Don't attempt to decode a compressed zip file |
951 // Don't attempt to decode a compressed zip file |
936 return $gzData; |
952 return $gz_data; |
937 } |
953 } |
938 |
954 |
939 // Determine the first byte of data, based on the above ZIP header |
955 // Determine the first byte of data, based on the above ZIP header |
940 // offsets: |
956 // offsets: |
941 $first_file_start = array_sum(unpack('v2', substr($gzData, 26, 4))); |
957 $first_file_start = array_sum(unpack('v2', substr($gz_data, 26, 4))); |
942 if (false !== ($decompressed = @gzinflate(substr($gzData, 30 + $first_file_start)))) { |
958 $decompressed = @gzinflate(substr($gz_data, 30 + $first_file_start)); |
|
959 if ($decompressed !== false) { |
943 return $decompressed; |
960 return $decompressed; |
944 } |
961 } |
945 return false; |
962 return false; |
946 } |
963 } |
947 |
964 |
948 // Finally fall back to straight gzinflate |
965 // Finally fall back to straight gzinflate |
949 if (false !== ($decompressed = @gzinflate($gzData))) { |
966 $decompressed = @gzinflate($gz_data); |
|
967 if ($decompressed !== false) { |
950 return $decompressed; |
968 return $decompressed; |
951 } |
969 } |
952 |
970 |
953 // Fallback for all above failing, not expected, but included for |
971 // Fallback for all above failing, not expected, but included for |
954 // debugging and preventing regressions and to track stats |
972 // debugging and preventing regressions and to track stats |
955 if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) { |
973 $decompressed = @gzinflate(substr($gz_data, 2)); |
|
974 if ($decompressed !== false) { |
956 return $decompressed; |
975 return $decompressed; |
957 } |
976 } |
958 |
977 |
959 return false; |
978 return false; |
960 } |
979 } |