PluginProbe ʕ •ᴥ•ʔ
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin / 3.7.0
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin v3.7.0
0.9.6 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.2 1.7.0 1.7.1 1.8.0 1.8.1 1.9.0 2.0.0 2.0.1 2.1.1 2.2.1 2.3.1 2.4.0 2.5.0 2.5.1 2.6.0 2.7.0 2.8.0 2.9.0 3.0.1 3.0.2 3.0.3 3.1.0 3.10.0 3.11.0 3.11.1 3.2.0 3.2.1 3.3.0 3.4.0 3.5.0 3.5.1 3.5.2 3.6.1 3.7.0 3.8.0 3.8.2 3.9.0 4.0.1 4.1.0 4.1.1 4.2.0 4.3.0 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.8.0 trunk 0.10.0 0.10.1 0.11.1 0.11.2 0.3.1 0.3.2 0.4 0.4.1 0.4.2 0.5.0 0.5.1 0.5.2 0.6 0.7 0.8 0.8.2 0.8.3 0.8.4 0.8.5 0.8.6 0.8.7 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5
wp-mail-smtp / vendor_prefixed / guzzlehttp / guzzle / src / Handler / CurlFactory.php
wp-mail-smtp / vendor_prefixed / guzzlehttp / guzzle / src / Handler Last commit date
CurlFactory.php 3 years ago CurlFactoryInterface.php 3 years ago CurlHandler.php 3 years ago CurlMultiHandler.php 3 years ago EasyHandle.php 3 years ago MockHandler.php 3 years ago Proxy.php 3 years ago StreamHandler.php 3 years ago
CurlFactory.php
437 lines
1 <?php
2
3 namespace WPMailSMTP\Vendor\GuzzleHttp\Handler;
4
5 use WPMailSMTP\Vendor\GuzzleHttp\Exception\ConnectException;
6 use WPMailSMTP\Vendor\GuzzleHttp\Exception\RequestException;
7 use WPMailSMTP\Vendor\GuzzleHttp\Promise\FulfilledPromise;
8 use WPMailSMTP\Vendor\GuzzleHttp\Psr7;
9 use WPMailSMTP\Vendor\GuzzleHttp\Psr7\LazyOpenStream;
10 use WPMailSMTP\Vendor\GuzzleHttp\TransferStats;
11 use WPMailSMTP\Vendor\Psr\Http\Message\RequestInterface;
12 /**
13 * Creates curl resources from a request
14 */
15 class CurlFactory implements \WPMailSMTP\Vendor\GuzzleHttp\Handler\CurlFactoryInterface
16 {
17 const CURL_VERSION_STR = 'curl_version';
18 const LOW_CURL_VERSION_NUMBER = '7.21.2';
19 /** @var array */
20 private $handles = [];
21 /** @var int Total number of idle handles to keep in cache */
22 private $maxHandles;
23 /**
24 * @param int $maxHandles Maximum number of idle handles.
25 */
26 public function __construct($maxHandles)
27 {
28 $this->maxHandles = $maxHandles;
29 }
30 public function create(\WPMailSMTP\Vendor\Psr\Http\Message\RequestInterface $request, array $options)
31 {
32 if (isset($options['curl']['body_as_string'])) {
33 $options['_body_as_string'] = $options['curl']['body_as_string'];
34 unset($options['curl']['body_as_string']);
35 }
36 $easy = new \WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle();
37 $easy->request = $request;
38 $easy->options = $options;
39 $conf = $this->getDefaultConf($easy);
40 $this->applyMethod($easy, $conf);
41 $this->applyHandlerOptions($easy, $conf);
42 $this->applyHeaders($easy, $conf);
43 unset($conf['_headers']);
44 // Add handler options from the request configuration options
45 if (isset($options['curl'])) {
46 $conf = \array_replace($conf, $options['curl']);
47 }
48 $conf[\CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
49 $easy->handle = $this->handles ? \array_pop($this->handles) : \curl_init();
50 \curl_setopt_array($easy->handle, $conf);
51 return $easy;
52 }
53 public function release(\WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy)
54 {
55 $resource = $easy->handle;
56 unset($easy->handle);
57 if (\count($this->handles) >= $this->maxHandles) {
58 \curl_close($resource);
59 } else {
60 // Remove all callback functions as they can hold onto references
61 // and are not cleaned up by curl_reset. Using curl_setopt_array
62 // does not work for some reason, so removing each one
63 // individually.
64 \curl_setopt($resource, \CURLOPT_HEADERFUNCTION, null);
65 \curl_setopt($resource, \CURLOPT_READFUNCTION, null);
66 \curl_setopt($resource, \CURLOPT_WRITEFUNCTION, null);
67 \curl_setopt($resource, \CURLOPT_PROGRESSFUNCTION, null);
68 \curl_reset($resource);
69 $this->handles[] = $resource;
70 }
71 }
72 /**
73 * Completes a cURL transaction, either returning a response promise or a
74 * rejected promise.
75 *
76 * @param callable $handler
77 * @param EasyHandle $easy
78 * @param CurlFactoryInterface $factory Dictates how the handle is released
79 *
80 * @return \GuzzleHttp\Promise\PromiseInterface
81 */
82 public static function finish(callable $handler, \WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy, \WPMailSMTP\Vendor\GuzzleHttp\Handler\CurlFactoryInterface $factory)
83 {
84 if (isset($easy->options['on_stats'])) {
85 self::invokeStats($easy);
86 }
87 if (!$easy->response || $easy->errno) {
88 return self::finishError($handler, $easy, $factory);
89 }
90 // Return the response if it is present and there is no error.
91 $factory->release($easy);
92 // Rewind the body of the response if possible.
93 $body = $easy->response->getBody();
94 if ($body->isSeekable()) {
95 $body->rewind();
96 }
97 return new \WPMailSMTP\Vendor\GuzzleHttp\Promise\FulfilledPromise($easy->response);
98 }
99 private static function invokeStats(\WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy)
100 {
101 $curlStats = \curl_getinfo($easy->handle);
102 $curlStats['appconnect_time'] = \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME);
103 $stats = new \WPMailSMTP\Vendor\GuzzleHttp\TransferStats($easy->request, $easy->response, $curlStats['total_time'], $easy->errno, $curlStats);
104 \call_user_func($easy->options['on_stats'], $stats);
105 }
106 private static function finishError(callable $handler, \WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy, \WPMailSMTP\Vendor\GuzzleHttp\Handler\CurlFactoryInterface $factory)
107 {
108 // Get error information and release the handle to the factory.
109 $ctx = ['errno' => $easy->errno, 'error' => \curl_error($easy->handle), 'appconnect_time' => \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME)] + \curl_getinfo($easy->handle);
110 $ctx[self::CURL_VERSION_STR] = \curl_version()['version'];
111 $factory->release($easy);
112 // Retry when nothing is present or when curl failed to rewind.
113 if (empty($easy->options['_err_message']) && (!$easy->errno || $easy->errno == 65)) {
114 return self::retryFailedRewind($handler, $easy, $ctx);
115 }
116 return self::createRejection($easy, $ctx);
117 }
118 private static function createRejection(\WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy, array $ctx)
119 {
120 static $connectionErrors = [\CURLE_OPERATION_TIMEOUTED => \true, \CURLE_COULDNT_RESOLVE_HOST => \true, \CURLE_COULDNT_CONNECT => \true, \CURLE_SSL_CONNECT_ERROR => \true, \CURLE_GOT_NOTHING => \true];
121 // If an exception was encountered during the onHeaders event, then
122 // return a rejected promise that wraps that exception.
123 if ($easy->onHeadersException) {
124 return \WPMailSMTP\Vendor\GuzzleHttp\Promise\rejection_for(new \WPMailSMTP\Vendor\GuzzleHttp\Exception\RequestException('An error was encountered during the on_headers event', $easy->request, $easy->response, $easy->onHeadersException, $ctx));
125 }
126 if (\version_compare($ctx[self::CURL_VERSION_STR], self::LOW_CURL_VERSION_NUMBER)) {
127 $message = \sprintf('cURL error %s: %s (%s)', $ctx['errno'], $ctx['error'], 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html');
128 } else {
129 $message = \sprintf('cURL error %s: %s (%s) for %s', $ctx['errno'], $ctx['error'], 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html', $easy->request->getUri());
130 }
131 // Create a connection exception if it was a specific error code.
132 $error = isset($connectionErrors[$easy->errno]) ? new \WPMailSMTP\Vendor\GuzzleHttp\Exception\ConnectException($message, $easy->request, null, $ctx) : new \WPMailSMTP\Vendor\GuzzleHttp\Exception\RequestException($message, $easy->request, $easy->response, null, $ctx);
133 return \WPMailSMTP\Vendor\GuzzleHttp\Promise\rejection_for($error);
134 }
135 private function getDefaultConf(\WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy)
136 {
137 $conf = ['_headers' => $easy->request->getHeaders(), \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), \CURLOPT_RETURNTRANSFER => \false, \CURLOPT_HEADER => \false, \CURLOPT_CONNECTTIMEOUT => 150];
138 if (\defined('CURLOPT_PROTOCOLS')) {
139 $conf[\CURLOPT_PROTOCOLS] = \CURLPROTO_HTTP | \CURLPROTO_HTTPS;
140 }
141 $version = $easy->request->getProtocolVersion();
142 if ($version == 1.1) {
143 $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1;
144 } elseif ($version == 2.0) {
145 $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0;
146 } else {
147 $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0;
148 }
149 return $conf;
150 }
151 private function applyMethod(\WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy, array &$conf)
152 {
153 $body = $easy->request->getBody();
154 $size = $body->getSize();
155 if ($size === null || $size > 0) {
156 $this->applyBody($easy->request, $easy->options, $conf);
157 return;
158 }
159 $method = $easy->request->getMethod();
160 if ($method === 'PUT' || $method === 'POST') {
161 // See http://tools.ietf.org/html/rfc7230#section-3.3.2
162 if (!$easy->request->hasHeader('Content-Length')) {
163 $conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
164 }
165 } elseif ($method === 'HEAD') {
166 $conf[\CURLOPT_NOBODY] = \true;
167 unset($conf[\CURLOPT_WRITEFUNCTION], $conf[\CURLOPT_READFUNCTION], $conf[\CURLOPT_FILE], $conf[\CURLOPT_INFILE]);
168 }
169 }
170 private function applyBody(\WPMailSMTP\Vendor\Psr\Http\Message\RequestInterface $request, array $options, array &$conf)
171 {
172 $size = $request->hasHeader('Content-Length') ? (int) $request->getHeaderLine('Content-Length') : null;
173 // Send the body as a string if the size is less than 1MB OR if the
174 // [curl][body_as_string] request value is set.
175 if ($size !== null && $size < 1000000 || !empty($options['_body_as_string'])) {
176 $conf[\CURLOPT_POSTFIELDS] = (string) $request->getBody();
177 // Don't duplicate the Content-Length header
178 $this->removeHeader('Content-Length', $conf);
179 $this->removeHeader('Transfer-Encoding', $conf);
180 } else {
181 $conf[\CURLOPT_UPLOAD] = \true;
182 if ($size !== null) {
183 $conf[\CURLOPT_INFILESIZE] = $size;
184 $this->removeHeader('Content-Length', $conf);
185 }
186 $body = $request->getBody();
187 if ($body->isSeekable()) {
188 $body->rewind();
189 }
190 $conf[\CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use($body) {
191 return $body->read($length);
192 };
193 }
194 // If the Expect header is not present, prevent curl from adding it
195 if (!$request->hasHeader('Expect')) {
196 $conf[\CURLOPT_HTTPHEADER][] = 'Expect:';
197 }
198 // cURL sometimes adds a content-type by default. Prevent this.
199 if (!$request->hasHeader('Content-Type')) {
200 $conf[\CURLOPT_HTTPHEADER][] = 'Content-Type:';
201 }
202 }
203 private function applyHeaders(\WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy, array &$conf)
204 {
205 foreach ($conf['_headers'] as $name => $values) {
206 foreach ($values as $value) {
207 $value = (string) $value;
208 if ($value === '') {
209 // cURL requires a special format for empty headers.
210 // See https://github.com/guzzle/guzzle/issues/1882 for more details.
211 $conf[\CURLOPT_HTTPHEADER][] = "{$name};";
212 } else {
213 $conf[\CURLOPT_HTTPHEADER][] = "{$name}: {$value}";
214 }
215 }
216 }
217 // Remove the Accept header if one was not set
218 if (!$easy->request->hasHeader('Accept')) {
219 $conf[\CURLOPT_HTTPHEADER][] = 'Accept:';
220 }
221 }
222 /**
223 * Remove a header from the options array.
224 *
225 * @param string $name Case-insensitive header to remove
226 * @param array $options Array of options to modify
227 */
228 private function removeHeader($name, array &$options)
229 {
230 foreach (\array_keys($options['_headers']) as $key) {
231 if (!\strcasecmp($key, $name)) {
232 unset($options['_headers'][$key]);
233 return;
234 }
235 }
236 }
237 private function applyHandlerOptions(\WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy, array &$conf)
238 {
239 $options = $easy->options;
240 if (isset($options['verify'])) {
241 if ($options['verify'] === \false) {
242 unset($conf[\CURLOPT_CAINFO]);
243 $conf[\CURLOPT_SSL_VERIFYHOST] = 0;
244 $conf[\CURLOPT_SSL_VERIFYPEER] = \false;
245 } else {
246 $conf[\CURLOPT_SSL_VERIFYHOST] = 2;
247 $conf[\CURLOPT_SSL_VERIFYPEER] = \true;
248 if (\is_string($options['verify'])) {
249 // Throw an error if the file/folder/link path is not valid or doesn't exist.
250 if (!\file_exists($options['verify'])) {
251 throw new \InvalidArgumentException("SSL CA bundle not found: {$options['verify']}");
252 }
253 // If it's a directory or a link to a directory use CURLOPT_CAPATH.
254 // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
255 if (\is_dir($options['verify']) || \is_link($options['verify']) && \is_dir(\readlink($options['verify']))) {
256 $conf[\CURLOPT_CAPATH] = $options['verify'];
257 } else {
258 $conf[\CURLOPT_CAINFO] = $options['verify'];
259 }
260 }
261 }
262 }
263 if (!empty($options['decode_content'])) {
264 $accept = $easy->request->getHeaderLine('Accept-Encoding');
265 if ($accept) {
266 $conf[\CURLOPT_ENCODING] = $accept;
267 } else {
268 $conf[\CURLOPT_ENCODING] = '';
269 // Don't let curl send the header over the wire
270 $conf[\CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
271 }
272 }
273 if (isset($options['sink'])) {
274 $sink = $options['sink'];
275 if (!\is_string($sink)) {
276 $sink = \WPMailSMTP\Vendor\GuzzleHttp\Psr7\stream_for($sink);
277 } elseif (!\is_dir(\dirname($sink))) {
278 // Ensure that the directory exists before failing in curl.
279 throw new \RuntimeException(\sprintf('Directory %s does not exist for sink value of %s', \dirname($sink), $sink));
280 } else {
281 $sink = new \WPMailSMTP\Vendor\GuzzleHttp\Psr7\LazyOpenStream($sink, 'w+');
282 }
283 $easy->sink = $sink;
284 $conf[\CURLOPT_WRITEFUNCTION] = function ($ch, $write) use($sink) {
285 return $sink->write($write);
286 };
287 } else {
288 // Use a default temp stream if no sink was set.
289 $conf[\CURLOPT_FILE] = \fopen('php://temp', 'w+');
290 $easy->sink = \WPMailSMTP\Vendor\GuzzleHttp\Psr7\stream_for($conf[\CURLOPT_FILE]);
291 }
292 $timeoutRequiresNoSignal = \false;
293 if (isset($options['timeout'])) {
294 $timeoutRequiresNoSignal |= $options['timeout'] < 1;
295 $conf[\CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
296 }
297 // CURL default value is CURL_IPRESOLVE_WHATEVER
298 if (isset($options['force_ip_resolve'])) {
299 if ('v4' === $options['force_ip_resolve']) {
300 $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V4;
301 } elseif ('v6' === $options['force_ip_resolve']) {
302 $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V6;
303 }
304 }
305 if (isset($options['connect_timeout'])) {
306 $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1;
307 $conf[\CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
308 }
309 if ($timeoutRequiresNoSignal && \strtoupper(\substr(\PHP_OS, 0, 3)) !== 'WIN') {
310 $conf[\CURLOPT_NOSIGNAL] = \true;
311 }
312 if (isset($options['proxy'])) {
313 if (!\is_array($options['proxy'])) {
314 $conf[\CURLOPT_PROXY] = $options['proxy'];
315 } else {
316 $scheme = $easy->request->getUri()->getScheme();
317 if (isset($options['proxy'][$scheme])) {
318 $host = $easy->request->getUri()->getHost();
319 if (!isset($options['proxy']['no']) || !\WPMailSMTP\Vendor\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no'])) {
320 $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme];
321 }
322 }
323 }
324 }
325 if (isset($options['cert'])) {
326 $cert = $options['cert'];
327 if (\is_array($cert)) {
328 $conf[\CURLOPT_SSLCERTPASSWD] = $cert[1];
329 $cert = $cert[0];
330 }
331 if (!\file_exists($cert)) {
332 throw new \InvalidArgumentException("SSL certificate not found: {$cert}");
333 }
334 $conf[\CURLOPT_SSLCERT] = $cert;
335 }
336 if (isset($options['ssl_key'])) {
337 if (\is_array($options['ssl_key'])) {
338 if (\count($options['ssl_key']) === 2) {
339 list($sslKey, $conf[\CURLOPT_SSLKEYPASSWD]) = $options['ssl_key'];
340 } else {
341 list($sslKey) = $options['ssl_key'];
342 }
343 }
344 $sslKey = isset($sslKey) ? $sslKey : $options['ssl_key'];
345 if (!\file_exists($sslKey)) {
346 throw new \InvalidArgumentException("SSL private key not found: {$sslKey}");
347 }
348 $conf[\CURLOPT_SSLKEY] = $sslKey;
349 }
350 if (isset($options['progress'])) {
351 $progress = $options['progress'];
352 if (!\is_callable($progress)) {
353 throw new \InvalidArgumentException('progress client option must be callable');
354 }
355 $conf[\CURLOPT_NOPROGRESS] = \false;
356 $conf[\CURLOPT_PROGRESSFUNCTION] = function () use($progress) {
357 $args = \func_get_args();
358 // PHP 5.5 pushed the handle onto the start of the args
359 if (\is_resource($args[0])) {
360 \array_shift($args);
361 }
362 \call_user_func_array($progress, $args);
363 };
364 }
365 if (!empty($options['debug'])) {
366 $conf[\CURLOPT_STDERR] = \WPMailSMTP\Vendor\GuzzleHttp\debug_resource($options['debug']);
367 $conf[\CURLOPT_VERBOSE] = \true;
368 }
369 }
370 /**
371 * This function ensures that a response was set on a transaction. If one
372 * was not set, then the request is retried if possible. This error
373 * typically means you are sending a payload, curl encountered a
374 * "Connection died, retrying a fresh connect" error, tried to rewind the
375 * stream, and then encountered a "necessary data rewind wasn't possible"
376 * error, causing the request to be sent through curl_multi_info_read()
377 * without an error status.
378 */
379 private static function retryFailedRewind(callable $handler, \WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy, array $ctx)
380 {
381 try {
382 // Only rewind if the body has been read from.
383 $body = $easy->request->getBody();
384 if ($body->tell() > 0) {
385 $body->rewind();
386 }
387 } catch (\RuntimeException $e) {
388 $ctx['error'] = 'The connection unexpectedly failed without ' . 'providing an error. The request would have been retried, ' . 'but attempting to rewind the request body failed. ' . 'Exception: ' . $e;
389 return self::createRejection($easy, $ctx);
390 }
391 // Retry no more than 3 times before giving up.
392 if (!isset($easy->options['_curl_retries'])) {
393 $easy->options['_curl_retries'] = 1;
394 } elseif ($easy->options['_curl_retries'] == 2) {
395 $ctx['error'] = 'The cURL request was retried 3 times ' . 'and did not succeed. The most likely reason for the failure ' . 'is that cURL was unable to rewind the body of the request ' . 'and subsequent retries resulted in the same error. Turn on ' . 'the debug option to see what went wrong. See ' . 'https://bugs.php.net/bug.php?id=47204 for more information.';
396 return self::createRejection($easy, $ctx);
397 } else {
398 $easy->options['_curl_retries']++;
399 }
400 return $handler($easy->request, $easy->options);
401 }
402 private function createHeaderFn(\WPMailSMTP\Vendor\GuzzleHttp\Handler\EasyHandle $easy)
403 {
404 if (isset($easy->options['on_headers'])) {
405 $onHeaders = $easy->options['on_headers'];
406 if (!\is_callable($onHeaders)) {
407 throw new \InvalidArgumentException('on_headers must be callable');
408 }
409 } else {
410 $onHeaders = null;
411 }
412 return function ($ch, $h) use($onHeaders, $easy, &$startingResponse) {
413 $value = \trim($h);
414 if ($value === '') {
415 $startingResponse = \true;
416 $easy->createResponse();
417 if ($onHeaders !== null) {
418 try {
419 $onHeaders($easy->response);
420 } catch (\Exception $e) {
421 // Associate the exception with the handle and trigger
422 // a curl header write error by returning 0.
423 $easy->onHeadersException = $e;
424 return -1;
425 }
426 }
427 } elseif ($startingResponse) {
428 $startingResponse = \false;
429 $easy->headers = [$value];
430 } else {
431 $easy->headers[] = $value;
432 }
433 return \strlen($h);
434 };
435 }
436 }
437