Diff: STRATO-apps/wordpress_03/app/wp-includes/class-wp-http-curl.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 + /**
3 + * HTTP API: WP_Http_Curl class
4 + *
5 + * @package WordPress
6 + * @subpackage HTTP
7 + * @since 4.4.0
8 + */
9 +
10 + /**
11 + * Core class used to integrate Curl as an HTTP transport.
12 + *
13 + * HTTP request method uses Curl extension to retrieve the url.
14 + *
15 + * Requires the Curl extension to be installed.
16 + *
17 + * @since 2.7.0
18 + * @deprecated 6.4.0 Use WP_Http
19 + * @see WP_Http
20 + */
21 + #[AllowDynamicProperties]
22 + class WP_Http_Curl {
23 +
24 + /**
25 + * Temporary header storage for during requests.
26 + *
27 + * @since 3.2.0
28 + * @var string
29 + */
30 + private $headers = '';
31 +
32 + /**
33 + * Temporary body storage for during requests.
34 + *
35 + * @since 3.6.0
36 + * @var string
37 + */
38 + private $body = '';
39 +
40 + /**
41 + * The maximum amount of data to receive from the remote server.
42 + *
43 + * @since 3.6.0
44 + * @var int|false
45 + */
46 + private $max_body_length = false;
47 +
48 + /**
49 + * The file resource used for streaming to file.
50 + *
51 + * @since 3.6.0
52 + * @var resource|false
53 + */
54 + private $stream_handle = false;
55 +
56 + /**
57 + * The total bytes written in the current request.
58 + *
59 + * @since 4.1.0
60 + * @var int
61 + */
62 + private $bytes_written_total = 0;
63 +
64 + /**
65 + * Send a HTTP request to a URI using cURL extension.
66 + *
67 + * @since 2.7.0
68 + *
69 + * @param string $url The request URL.
70 + * @param string|array $args Optional. Override the defaults.
71 + * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
72 + */
73 + public function request( $url, $args = array() ) {
74 + $defaults = array(
75 + 'method' => 'GET',
76 + 'timeout' => 5,
77 + 'redirection' => 5,
78 + 'httpversion' => '1.0',
79 + 'blocking' => true,
80 + 'headers' => array(),
81 + 'body' => null,
82 + 'cookies' => array(),
83 + 'decompress' => false,
84 + 'stream' => false,
85 + 'filename' => null,
86 + );
87 +
88 + $parsed_args = wp_parse_args( $args, $defaults );
89 +
90 + if ( isset( $parsed_args['headers']['User-Agent'] ) ) {
91 + $parsed_args['user-agent'] = $parsed_args['headers']['User-Agent'];
92 + unset( $parsed_args['headers']['User-Agent'] );
93 + } elseif ( isset( $parsed_args['headers']['user-agent'] ) ) {
94 + $parsed_args['user-agent'] = $parsed_args['headers']['user-agent'];
95 + unset( $parsed_args['headers']['user-agent'] );
96 + }
97 +
98 + // Construct Cookie: header if any cookies are set.
99 + WP_Http::buildCookieHeader( $parsed_args );
100 +
101 + $handle = curl_init();
102 +
103 + // cURL offers really easy proxy support.
104 + $proxy = new WP_HTTP_Proxy();
105 +
106 + if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
107 +
108 + curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
109 + curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() );
110 + curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() );
111 +
112 + if ( $proxy->use_authentication() ) {
113 + curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
114 + curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
115 + }
116 + }
117 +
118 + $is_local = isset( $parsed_args['local'] ) && $parsed_args['local'];
119 + $ssl_verify = isset( $parsed_args['sslverify'] ) && $parsed_args['sslverify'];
120 + if ( $is_local ) {
121 + /** This filter is documented in wp-includes/class-wp-http-streams.php */
122 + $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify, $url );
123 + } elseif ( ! $is_local ) {
124 + /** This filter is documented in wp-includes/class-wp-http.php */
125 + $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify, $url );
126 + }
127 +
128 + /*
129 + * CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since.
130 + * a value of 0 will allow an unlimited timeout.
131 + */
132 + $timeout = (int) ceil( $parsed_args['timeout'] );
133 + curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout );
134 + curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout );
135 +
136 + curl_setopt( $handle, CURLOPT_URL, $url );
137 + curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true );
138 + curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( true === $ssl_verify ) ? 2 : false );
139 + curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify );
140 +
141 + if ( $ssl_verify ) {
142 + curl_setopt( $handle, CURLOPT_CAINFO, $parsed_args['sslcertificates'] );
143 + }
144 +
145 + curl_setopt( $handle, CURLOPT_USERAGENT, $parsed_args['user-agent'] );
146 +
147 + /*
148 + * The option doesn't work with safe mode or when open_basedir is set, and there's
149 + * a bug #17490 with redirected POST requests, so handle redirections outside Curl.
150 + */
151 + curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false );
152 + curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
153 +
154 + switch ( $parsed_args['method'] ) {
155 + case 'HEAD':
156 + curl_setopt( $handle, CURLOPT_NOBODY, true );
157 + break;
158 + case 'POST':
159 + curl_setopt( $handle, CURLOPT_POST, true );
160 + curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] );
161 + break;
162 + case 'PUT':
163 + curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' );
164 + curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] );
165 + break;
166 + default:
167 + curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $parsed_args['method'] );
168 + if ( ! is_null( $parsed_args['body'] ) ) {
169 + curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] );
170 + }
171 + break;
172 + }
173 +
174 + if ( true === $parsed_args['blocking'] ) {
175 + curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) );
176 + curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) );
177 + }
178 +
179 + curl_setopt( $handle, CURLOPT_HEADER, false );
180 +
181 + if ( isset( $parsed_args['limit_response_size'] ) ) {
182 + $this->max_body_length = (int) $parsed_args['limit_response_size'];
183 + } else {
184 + $this->max_body_length = false;
185 + }
186 +
187 + // If streaming to a file open a file handle, and setup our curl streaming handler.
188 + if ( $parsed_args['stream'] ) {
189 + if ( ! WP_DEBUG ) {
190 + $this->stream_handle = @fopen( $parsed_args['filename'], 'w+' );
191 + } else {
192 + $this->stream_handle = fopen( $parsed_args['filename'], 'w+' );
193 + }
194 + if ( ! $this->stream_handle ) {
195 + return new WP_Error(
196 + 'http_request_failed',
197 + sprintf(
198 + /* translators: 1: fopen(), 2: File name. */
199 + __( 'Could not open handle for %1$s to %2$s.' ),
200 + 'fopen()',
201 + $parsed_args['filename']
202 + )
203 + );
204 + }
205 + } else {
206 + $this->stream_handle = false;
207 + }
208 +
209 + if ( ! empty( $parsed_args['headers'] ) ) {
210 + // cURL expects full header strings in each element.
211 + $headers = array();
212 + foreach ( $parsed_args['headers'] as $name => $value ) {
213 + $headers[] = "{$name}: $value";
214 + }
215 + curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
216 + }
217 +
218 + if ( '1.0' === $parsed_args['httpversion'] ) {
219 + curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
220 + } else {
221 + curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
222 + }
223 +
224 + /**
225 + * Fires before the cURL request is executed.
226 + *
227 + * Cookies are not currently handled by the HTTP API. This action allows
228 + * plugins to handle cookies themselves.
229 + *
230 + * @since 2.8.0
231 + *
232 + * @param resource $handle The cURL handle returned by curl_init() (passed by reference).
233 + * @param array $parsed_args The HTTP request arguments.
234 + * @param string $url The request URL.
235 + */
236 + do_action_ref_array( 'http_api_curl', array( &$handle, $parsed_args, $url ) );
237 +
238 + // We don't need to return the body, so don't. Just execute request and return.
239 + if ( ! $parsed_args['blocking'] ) {
240 + curl_exec( $handle );
241 +
242 + $curl_error = curl_error( $handle );
243 +
244 + if ( $curl_error ) {
245 + if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0.
246 + curl_close( $handle );
247 + }
248 +
249 + return new WP_Error( 'http_request_failed', $curl_error );
250 + }
251 +
252 + if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ), true ) ) {
253 + if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0.
254 + curl_close( $handle );
255 + }
256 +
257 + return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
258 + }
259 +
260 + if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0.
261 + curl_close( $handle );
262 + }
263 +
264 + return array(
265 + 'headers' => array(),
266 + 'body' => '',
267 + 'response' => array(
268 + 'code' => false,
269 + 'message' => false,
270 + ),
271 + 'cookies' => array(),
272 + );
273 + }
274 +
275 + curl_exec( $handle );
276 +
277 + $processed_headers = WP_Http::processHeaders( $this->headers, $url );
278 + $body = $this->body;
279 + $bytes_written_total = $this->bytes_written_total;
280 +
281 + $this->headers = '';
282 + $this->body = '';
283 + $this->bytes_written_total = 0;
284 +
285 + $curl_error = curl_errno( $handle );
286 +
287 + // If an error occurred, or, no response.
288 + if ( $curl_error || ( 0 === strlen( $body ) && empty( $processed_headers['headers'] ) ) ) {
289 + if ( CURLE_WRITE_ERROR /* 23 */ === $curl_error ) {
290 + if ( ! $this->max_body_length || $this->max_body_length !== $bytes_written_total ) {
291 + if ( $parsed_args['stream'] ) {
292 + if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0.
293 + curl_close( $handle );
294 + }
295 +
296 + fclose( $this->stream_handle );
297 +
298 + return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) );
299 + } else {
300 + if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0.
301 + curl_close( $handle );
302 + }
303 +
304 + return new WP_Error( 'http_request_failed', curl_error( $handle ) );
305 + }
306 + }
307 + } else {
308 + $curl_error = curl_error( $handle );
309 +
310 + if ( $curl_error ) {
311 + if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0.
312 + curl_close( $handle );
313 + }
314 +
315 + return new WP_Error( 'http_request_failed', $curl_error );
316 + }
317 + }
318 +
319 + if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ), true ) ) {
320 + if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0.
321 + curl_close( $handle );
322 + }
323 +
324 + return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
325 + }
326 + }
327 +
328 + if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0.
329 + curl_close( $handle );
330 + }
331 +
332 + if ( $parsed_args['stream'] ) {
333 + fclose( $this->stream_handle );
334 + }
335 +
336 + $response = array(
337 + 'headers' => $processed_headers['headers'],
338 + 'body' => null,
339 + 'response' => $processed_headers['response'],
340 + 'cookies' => $processed_headers['cookies'],
341 + 'filename' => $parsed_args['filename'],
342 + );
343 +
344 + // Handle redirects.
345 + $redirect_response = WP_Http::handle_redirects( $url, $parsed_args, $response );
346 + if ( false !== $redirect_response ) {
347 + return $redirect_response;
348 + }
349 +
350 + if ( true === $parsed_args['decompress']
351 + && true === WP_Http_Encoding::should_decode( $processed_headers['headers'] )
352 + ) {
353 + $body = WP_Http_Encoding::decompress( $body );
354 + }
355 +
356 + $response['body'] = $body;
357 +
358 + return $response;
359 + }
360 +
361 + /**
362 + * Grabs the headers of the cURL request.
363 + *
364 + * Each header is sent individually to this callback, and is appended to the `$header` property
365 + * for temporary storage.
366 + *
367 + * @since 3.2.0
368 + *
369 + * @param resource $handle cURL handle.
370 + * @param string $headers cURL request headers.
371 + * @return int Length of the request headers.
372 + */
373 + private function stream_headers( $handle, $headers ) {
374 + $this->headers .= $headers;
375 + return strlen( $headers );
376 + }
377 +
378 + /**
379 + * Grabs the body of the cURL request.
380 + *
381 + * The contents of the document are passed in chunks, and are appended to the `$body`
382 + * property for temporary storage. Returning a length shorter than the length of
383 + * `$data` passed in will cause cURL to abort the request with `CURLE_WRITE_ERROR`.
384 + *
385 + * @since 3.6.0
386 + *
387 + * @param resource $handle cURL handle.
388 + * @param string $data cURL request body.
389 + * @return int Total bytes of data written.
390 + */
391 + private function stream_body( $handle, $data ) {
392 + $data_length = strlen( $data );
393 +
394 + if ( $this->max_body_length && ( $this->bytes_written_total + $data_length ) > $this->max_body_length ) {
395 + $data_length = ( $this->max_body_length - $this->bytes_written_total );
396 + $data = substr( $data, 0, $data_length );
397 + }
398 +
399 + if ( $this->stream_handle ) {
400 + $bytes_written = fwrite( $this->stream_handle, $data );
401 + } else {
402 + $this->body .= $data;
403 + $bytes_written = $data_length;
404 + }
405 +
406 + $this->bytes_written_total += $bytes_written;
407 +
408 + // Upon event of this function returning less than strlen( $data ) curl will error with CURLE_WRITE_ERROR.
409 + return $bytes_written;
410 + }
411 +
412 + /**
413 + * Determines whether this class can be used for retrieving a URL.
414 + *
415 + * @since 2.7.0
416 + *
417 + * @param array $args Optional. Array of request arguments. Default empty array.
418 + * @return bool False means this class can not be used, true means it can.
419 + */
420 + public static function test( $args = array() ) {
421 + if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) {
422 + return false;
423 + }
424 +
425 + $is_ssl = isset( $args['ssl'] ) && $args['ssl'];
426 +
427 + if ( $is_ssl ) {
428 + $curl_version = curl_version();
429 + // Check whether this cURL version support SSL requests.
430 + if ( ! ( CURL_VERSION_SSL & $curl_version['features'] ) ) {
431 + return false;
432 + }
433 + }
434 +
435 + /**
436 + * Filters whether cURL can be used as a transport for retrieving a URL.
437 + *
438 + * @since 2.7.0
439 + *
440 + * @param bool $use_class Whether the class can be used. Default true.
441 + * @param array $args An array of request arguments.
442 + */
443 + return apply_filters( 'use_curl_transport', true, $args );
444 + }
445 + }
446 +