From 8dfa2760f0d74cd16ef1a2357506abc33179671a Mon Sep 17 00:00:00 2001 From: Baoshuo Date: Tue, 31 Jan 2023 07:59:24 +0800 Subject: [PATCH] feat(problem/remote): download remote images --- web/app/composer.json | 2 +- web/app/controllers/new_remote_problem.php | 4 +- .../controllers/problem_statement_manage.php | 4 +- web/app/models/UOJRemoteProblem.php | 105 +- web/app/vendor/composer/autoload_classmap.php | 2 - web/app/vendor/composer/autoload_psr4.php | 1 + web/app/vendor/composer/autoload_static.php | 10 +- web/app/vendor/composer/installed.json | 69 +- web/app/vendor/composer/installed.php | 10 +- .../php-curl-class/php-curl-class/.gitignore | 2 - .../php-curl-class/php-curl-class/.travis.yml | 19 - .../php-curl-class/CHANGELOG.md | 214 ++ .../php-curl-class/php-curl-class/README.md | 362 ++- .../php-curl-class/php-curl-class/SECURITY.md | 108 + .../php-curl-class/composer.json | 39 +- .../examples/coinbase_account_balance.php | 22 - .../examples/coinbase_spot_rate.php | 10 - .../examples/instagram_search_photos.php | 17 - .../php-curl-class/examples/put.php | 14 - .../examples/twitter_post_tweet.php | 35 - .../php-curl-class/src/Curl.class.php | 496 ---- .../php-curl-class/src/Curl/ArrayUtil.php | 170 ++ .../php-curl-class/src/Curl/BaseCurl.php | 395 ++++ .../src/Curl/CaseInsensitiveArray.php | 245 ++ .../php-curl-class/src/Curl/Curl.php | 2057 +++++++++++++++++ .../php-curl-class/src/Curl/Decoder.php | 45 + .../php-curl-class/src/Curl/Encoder.php | 30 + .../php-curl-class/src/Curl/MultiCurl.php | 990 ++++++++ .../php-curl-class/src/Curl/StringUtil.php | 65 + .../php-curl-class/src/Curl/Url.php | 247 ++ .../tests/PHPCurlClass/PHPCurlClassTest.php | 740 ------ .../tests/PHPCurlClass/helper.inc.php | 47 - .../tests/PHPCurlClass/index.php | 1 - .../tests/PHPCurlClass/server.hdf | 7 - .../tests/PHPCurlClass/server.php | 132 -- .../php-curl-class/tests/phpunit.xml | 8 - .../php-curl-class/tests/run.sh | 4 - .../php-curl-class/tests/syntax.sh | 1 - 38 files changed, 5052 insertions(+), 1677 deletions(-) delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/.gitignore delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/.travis.yml create mode 100644 web/app/vendor/php-curl-class/php-curl-class/CHANGELOG.md create mode 100644 web/app/vendor/php-curl-class/php-curl-class/SECURITY.md delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_account_balance.php delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_spot_rate.php delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/examples/instagram_search_photos.php delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/examples/put.php delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/examples/twitter_post_tweet.php delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/src/Curl.class.php create mode 100644 web/app/vendor/php-curl-class/php-curl-class/src/Curl/ArrayUtil.php create mode 100644 web/app/vendor/php-curl-class/php-curl-class/src/Curl/BaseCurl.php create mode 100644 web/app/vendor/php-curl-class/php-curl-class/src/Curl/CaseInsensitiveArray.php create mode 100644 web/app/vendor/php-curl-class/php-curl-class/src/Curl/Curl.php create mode 100644 web/app/vendor/php-curl-class/php-curl-class/src/Curl/Decoder.php create mode 100644 web/app/vendor/php-curl-class/php-curl-class/src/Curl/Encoder.php create mode 100755 web/app/vendor/php-curl-class/php-curl-class/src/Curl/MultiCurl.php create mode 100644 web/app/vendor/php-curl-class/php-curl-class/src/Curl/StringUtil.php create mode 100644 web/app/vendor/php-curl-class/php-curl-class/src/Curl/Url.php delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/PHPCurlClassTest.php delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/helper.inc.php delete mode 120000 web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/index.php delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.hdf delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.php delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/tests/phpunit.xml delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/tests/run.sh delete mode 100644 web/app/vendor/php-curl-class/php-curl-class/tests/syntax.sh diff --git a/web/app/composer.json b/web/app/composer.json index 7ebf2b0..c857c4d 100644 --- a/web/app/composer.json +++ b/web/app/composer.json @@ -4,7 +4,7 @@ "phpmailer/phpmailer": "^6.6", "ezyang/htmlpurifier": "^4.16", "erusev/parsedown": "^1.7", - "php-curl-class/php-curl-class": "^2.0", + "php-curl-class/php-curl-class": "^9.13", "ext-dom": "20031129", "ivopetkov/html5-dom-document-php": "2.*" }, diff --git a/web/app/controllers/new_remote_problem.php b/web/app/controllers/new_remote_problem.php index f5f750b..63cf369 100644 --- a/web/app/controllers/new_remote_problem.php +++ b/web/app/controllers/new_remote_problem.php @@ -101,7 +101,7 @@ $new_remote_problem_form->handle = function (&$vdata) { dataNewProblem($id); if ($data['type'] == 'pdf') { - file_put_contents(UOJContext::storagePath() , "/problem_resources/$id/statement.pdf", $data['pdf_data']); + file_put_contents(UOJContext::storagePath(), "/problem_resources/$id/statement.pdf", $data['pdf_data']); $data['statement'] = "
\n" . $data['statement']; } @@ -119,6 +119,8 @@ $new_remote_problem_form->handle = function (&$vdata) { DB::tuple([$id, $remote_provider['name']]), ]); + UOJRemoteProblem::downloadImagesInRemoteContent(strval($id)); + redirectTo("/problem/{$id}"); die(); }; diff --git a/web/app/controllers/problem_statement_manage.php b/web/app/controllers/problem_statement_manage.php index e4a6d77..55dcb6a 100644 --- a/web/app/controllers/problem_statement_manage.php +++ b/web/app/controllers/problem_statement_manage.php @@ -152,13 +152,15 @@ if (UOJProblem::info('type') == 'remote') { DB::update([ "update problems_contents", "set", [ - "remote_content" => HTML::purifier()->purify($data['statement']), + "remote_content" => HTML::purifier(['a' => ['target' => 'Enum#_blank']])->purify($data['statement']), ], "where", [ "id" => UOJProblem::info('id'), ], ]); + UOJRemoteProblem::downloadImagesInRemoteContent(UOJProblem::info('id')); + redirectTo(UOJProblem::cur()->getUri()); }; $re_crawl_form->runAtServer(); diff --git a/web/app/models/UOJRemoteProblem.php b/web/app/models/UOJRemoteProblem.php index 9347620..304bee5 100644 --- a/web/app/models/UOJRemoteProblem.php +++ b/web/app/models/UOJRemoteProblem.php @@ -1,6 +1,8 @@ [ 'name' => 'Codeforces', @@ -40,6 +42,26 @@ class UOJRemoteProblem { ], ]; + static function curl_get($url) { + $curl = new Curl\Curl(); + $curl->setUserAgent(static::USER_AGENT); + + $res = retry_loop(function () use (&$curl, $url) { + $curl->get($url); + + if ($curl->error) { + return false; + } + + return [ + 'content-type' => $curl->response_headers['Content-Type'], + 'response' => $curl->response, + ]; + }); + + return $res; + } + static function getCodeforcesProblemUrl($id) { if (str_starts_with($id, 'GYM')) { return static::$providers['codeforces']['url'] . '/gym/' . preg_replace_callback('/GYM([1-9][0-9]{0,5})([A-Z][1-9]?)/', fn ($matches) => $matches[1] . '/problem/' . $matches[2], $id); @@ -165,21 +187,7 @@ class UOJRemoteProblem { } static function getCodeforcesProblemBasicInfo($id) { - $curl = new Curl(); - $curl->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36 S2OJ/3.1.0'); - - $res = retry_loop(function () use (&$curl, $id) { - $curl->get(static::getCodeforcesProblemUrl($id)); - - if ($curl->error) { - return false; - } - - return [ - 'content-type' => $curl->response_headers['Content-Type'], - 'response' => $curl->response, - ]; - }); + $res = static::curl_get(static::getCodeforcesProblemUrl($id)); if (!$res) return null; @@ -212,19 +220,7 @@ class UOJRemoteProblem { } static function getAtcoderProblemBasicInfo($id) { - $curl = new Curl(); - $curl->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36 S2OJ/3.1.0'); - $curl->setCookie('language', 'en'); - - $res = retry_loop(function () use (&$curl, $id) { - $curl->get(static::getAtcoderProblemUrl($id)); - - if ($curl->error) { - return false; - } - - return $curl->response; - }); + $res = static::curl_get(static::getAtcoderProblemUrl($id)); if (!$res) return null; @@ -299,18 +295,7 @@ class UOJRemoteProblem { static function getUojProblemBasicInfo($id) { $remote_provider = static::$providers['uoj']; - $curl = new Curl(); - $curl->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36 S2OJ/3.1.0'); - - $res = retry_loop(function () use (&$curl, $id) { - $curl->get(static::getUojProblemUrl($id)); - - if ($curl->error) { - return false; - } - - return $curl->response; - }); + $res = static::curl_get(static::getUojProblemUrl($id)); if (!$res) return null; @@ -345,8 +330,8 @@ class UOJRemoteProblem { static function getLojProblemBasicInfo($id) { $remote_provider = static::$providers['loj']; - $curl = new Curl(); - $curl->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36 S2OJ/3.1.0'); + $curl = new Curl\Curl(); + $curl->setUserAgent(static::USER_AGENT); $curl->setHeader('Content-Type', 'application/json'); $res = retry_loop(function () use (&$curl, $id) { @@ -405,7 +390,7 @@ class UOJRemoteProblem { 'time_limit' => (float)$res['judgeInfo']['timeLimit'] / 1000.0, 'memory_limit' => $res['judgeInfo']['memoryLimit'], 'difficulty' => -1, - 'statement' => HTML::purifier()->purify(HTML::parsedown()->text($statement)), + 'statement' => HTML::parsedown()->text($statement), ]; } @@ -437,4 +422,38 @@ class UOJRemoteProblem { return null; } + + public static function downloadImagesInRemoteContent($problem_id) { + $curl = new Curl\Curl(); + $curl->setUserAgent(static::USER_AGENT); + $curl->setRetry(5); + + $problem = UOJProblem::query($problem_id); + + if ($problem->info['type'] != 'remote') return; + + $remote_provider = static::$providers[$problem->getExtraConfig('remote_online_judge')]; + $remote_content = $problem->queryContent()['remote_content']; + + $dom = new IvoPetkov\HTML5DOMDocument(); + $dom->loadHTML($remote_content); + + foreach ($dom->querySelectorAll('img') as &$elem) { + $src = $elem->getAttribute('src'); + $url = getAbsoluteUrl($src, $remote_provider['url']); + $filename = 'remote_image_' . hash('md5', $url); + $curl->download($url, $problem->getResourcesPath($filename)); + $elem->setAttribute('src', $problem->getResourcesUri($filename)); + } + + DB::update([ + "update problems_contents", + "set", [ + "remote_content" => HTML::purifier(['a' => ['target' => 'Enum#_blank']])->purify($dom->saveHTML()), + ], + "where", [ + "id" => $problem->info['id'], + ], + ]); + } } diff --git a/web/app/vendor/composer/autoload_classmap.php b/web/app/vendor/composer/autoload_classmap.php index e047a6e..f3791df 100644 --- a/web/app/vendor/composer/autoload_classmap.php +++ b/web/app/vendor/composer/autoload_classmap.php @@ -7,9 +7,7 @@ $baseDir = dirname($vendorDir); return array( 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', - 'CaseInsensitiveArray' => $vendorDir . '/php-curl-class/php-curl-class/src/Curl.class.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', - 'Curl' => $vendorDir . '/php-curl-class/php-curl-class/src/Curl.class.php', 'ParsedownMath' => $vendorDir . '/parsedown-math/ParsedownMath.php', 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', diff --git a/web/app/vendor/composer/autoload_psr4.php b/web/app/vendor/composer/autoload_psr4.php index d7892aa..6f487a5 100644 --- a/web/app/vendor/composer/autoload_psr4.php +++ b/web/app/vendor/composer/autoload_psr4.php @@ -10,4 +10,5 @@ return array( 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), 'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'), 'Gregwar\\' => array($vendorDir . '/gregwar/captcha/src/Gregwar'), + 'Curl\\' => array($vendorDir . '/php-curl-class/php-curl-class/src/Curl'), ); diff --git a/web/app/vendor/composer/autoload_static.php b/web/app/vendor/composer/autoload_static.php index 506d13a..87966e2 100644 --- a/web/app/vendor/composer/autoload_static.php +++ b/web/app/vendor/composer/autoload_static.php @@ -27,6 +27,10 @@ class ComposerStaticInit0d7c2cd5c2dbf2120e4372996869e900 array ( 'Gregwar\\' => 8, ), + 'C' => + array ( + 'Curl\\' => 5, + ), ); public static $prefixDirsPsr4 = array ( @@ -46,6 +50,10 @@ class ComposerStaticInit0d7c2cd5c2dbf2120e4372996869e900 array ( 0 => __DIR__ . '/..' . '/gregwar/captcha/src/Gregwar', ), + 'Curl\\' => + array ( + 0 => __DIR__ . '/..' . '/php-curl-class/php-curl-class/src/Curl', + ), ); public static $prefixesPsr0 = array ( @@ -67,9 +75,7 @@ class ComposerStaticInit0d7c2cd5c2dbf2120e4372996869e900 public static $classMap = array ( 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', - 'CaseInsensitiveArray' => __DIR__ . '/..' . '/php-curl-class/php-curl-class/src/Curl.class.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', - 'Curl' => __DIR__ . '/..' . '/php-curl-class/php-curl-class/src/Curl.class.php', 'ParsedownMath' => __DIR__ . '/..' . '/parsedown-math/ParsedownMath.php', 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', diff --git a/web/app/vendor/composer/installed.json b/web/app/vendor/composer/installed.json index 4b3dccd..483f472 100644 --- a/web/app/vendor/composer/installed.json +++ b/web/app/vendor/composer/installed.json @@ -215,32 +215,79 @@ }, { "name": "php-curl-class/php-curl-class", - "version": "2.0.0", - "version_normalized": "2.0.0.0", + "version": "9.13.1", + "version_normalized": "9.13.1.0", "source": { "type": "git", "url": "https://github.com/php-curl-class/php-curl-class.git", - "reference": "24a93bdc51058ad50d219842b63f7f2e0cb350ac" + "reference": "1fa71f639275987092c60ac117ff5c8a3771898d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-curl-class/php-curl-class/zipball/24a93bdc51058ad50d219842b63f7f2e0cb350ac", - "reference": "24a93bdc51058ad50d219842b63f7f2e0cb350ac", + "url": "https://api.github.com/repos/php-curl-class/php-curl-class/zipball/1fa71f639275987092c60ac117ff5c8a3771898d", + "reference": "1fa71f639275987092c60ac117ff5c8a3771898d", "shasum": "" }, - "time": "2014-04-12T09:46:33+00:00", + "require": { + "ext-curl": "*", + "php": ">=7.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "*", + "ext-gd": "*", + "phpcompatibility/php-compatibility": "dev-develop", + "phpcsstandards/phpcsutils": "@alpha", + "phpunit/phpunit": "*", + "squizlabs/php_codesniffer": "*", + "vimeo/psalm": "*" + }, + "suggest": { + "ext-mbstring": "*" + }, + "time": "2023-01-16T01:29:25+00:00", "type": "library", "installation-source": "dist", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Curl\\": "src/Curl/" + } }, "notification-url": "https://packagist.org/downloads/", - "description": "PHP Curl Class is an object-oriented wrapper of the PHP cURL extension.", + "license": [ + "Unlicense" + ], + "authors": [ + { + "name": "Zach Borboa" + } + ], + "description": "PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs.", + "homepage": "https://github.com/php-curl-class/php-curl-class", + "keywords": [ + "API-Client", + "api", + "class", + "client", + "curl", + "framework", + "http", + "http-client", + "http-proxy", + "json", + "php", + "php-curl", + "php-curl-library", + "proxy", + "requests", + "restful", + "web-scraper", + "web-scraping ", + "web-service", + "xml" + ], "support": { "issues": "https://github.com/php-curl-class/php-curl-class/issues", - "source": "https://github.com/php-curl-class/php-curl-class/tree/2.0.0" + "source": "https://github.com/php-curl-class/php-curl-class/tree/9.13.1" }, "install-path": "../php-curl-class/php-curl-class" }, diff --git a/web/app/vendor/composer/installed.php b/web/app/vendor/composer/installed.php index 32b055e..e4ba758 100644 --- a/web/app/vendor/composer/installed.php +++ b/web/app/vendor/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'd6997b84758fbe52d9d90a2d5fe2f2e06806b176', + 'reference' => 'aa6bf6a363501f9836d2bb26a513c2ac3985de83', 'name' => '__root__', 'dev' => true, ), @@ -16,7 +16,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'd6997b84758fbe52d9d90a2d5fe2f2e06806b176', + 'reference' => 'aa6bf6a363501f9836d2bb26a513c2ac3985de83', 'dev_requirement' => false, ), 'erusev/parsedown' => array( @@ -56,12 +56,12 @@ 'dev_requirement' => false, ), 'php-curl-class/php-curl-class' => array( - 'pretty_version' => '2.0.0', - 'version' => '2.0.0.0', + 'pretty_version' => '9.13.1', + 'version' => '9.13.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../php-curl-class/php-curl-class', 'aliases' => array(), - 'reference' => '24a93bdc51058ad50d219842b63f7f2e0cb350ac', + 'reference' => '1fa71f639275987092c60ac117ff5c8a3771898d', 'dev_requirement' => false, ), 'phpmailer/phpmailer' => array( diff --git a/web/app/vendor/php-curl-class/php-curl-class/.gitignore b/web/app/vendor/php-curl-class/php-curl-class/.gitignore deleted file mode 100644 index d1502b0..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -vendor/ -composer.lock diff --git a/web/app/vendor/php-curl-class/php-curl-class/.travis.yml b/web/app/vendor/php-curl-class/php-curl-class/.travis.yml deleted file mode 100644 index a915771..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: php -php: - - 5.3 - - 5.4 - - 5.5 - - 5.6 - - hhvm -matrix: - allow_failures: - - php: 5.6 - - php: hhvm -before_script: - - if [[ "$TRAVIS_PHP_VERSION" == "5.4" ]]; then sh -c "php -S 127.0.0.1:8000 -t tests/PHPCurlClass/ &"; fi - - if [[ "$TRAVIS_PHP_VERSION" == "5.5" ]]; then sh -c "php -S 127.0.0.1:8000 -t tests/PHPCurlClass/ &"; fi - - if [[ "$TRAVIS_PHP_VERSION" == "5.6" ]]; then sh -c "php -S 127.0.0.1:8000 -t tests/PHPCurlClass/ &"; fi - - if [[ "$TRAVIS_PHP_VERSION" == "hhvm" ]]; then sh -c "cd tests && hhvm --mode server --port 8000 --config PHPCurlClass/server.hdf &"; fi -script: - - php -l src/* - - if [[ "$TRAVIS_PHP_VERSION" != "5.3" ]]; then sh -c "cd tests && phpunit --configuration phpunit.xml"; fi diff --git a/web/app/vendor/php-curl-class/php-curl-class/CHANGELOG.md b/web/app/vendor/php-curl-class/php-curl-class/CHANGELOG.md new file mode 100644 index 0000000..677c5e8 --- /dev/null +++ b/web/app/vendor/php-curl-class/php-curl-class/CHANGELOG.md @@ -0,0 +1,214 @@ +# Change Log + +PHP Curl Class uses semantic versioning with version numbers written as `MAJOR.MINOR.PATCH`. You may safely update +`MINOR` and `PATCH` version changes. It is recommended to review `MAJOR` changes prior to upgrade as there may be +backwards-incompatible changes that will affect existing usage. + + + +## 9.13.1 - 2023-01-16 + +- Allow uploads with CURLStringFile type ([#762](https://github.com/php-curl-class/php-curl-class/pull/762)) + +## 9.13.0 - 2023-01-13 + +- Implement abstract class BaseCurl for Curl and MultiCurl ([#759](https://github.com/php-curl-class/php-curl-class/pull/759)) +- Display error messages found in Curl::diagnose() ([#758](https://github.com/php-curl-class/php-curl-class/pull/758)) +- Fix Curl::diagnose() request type output for POST requests ([#757](https://github.com/php-curl-class/php-curl-class/pull/757)) + +## 9.12.6 - 2023-01-11 + +- Replace use of #[\AllowDynamicProperties] ([#756](https://github.com/php-curl-class/php-curl-class/pull/756)) +- silence PHP 8.2 deprecation notices ([#754](https://github.com/php-curl-class/php-curl-class/pull/754)) + +## 9.12.5 - 2022-12-20 + +- Fix static analysis error ([#752](https://github.com/php-curl-class/php-curl-class/pull/752)) + +## 9.12.4 - 2022-12-17 + +- Exclude additional files from git archive ([#751](https://github.com/php-curl-class/php-curl-class/pull/751)) + +## 9.12.3 - 2022-12-13 + +- Ensure string response before gzip decode ([#749](https://github.com/php-curl-class/php-curl-class/pull/749)) + +## 9.12.2 - 2022-12-11 + +- Disable warning when gzip-decoding response errors ([#748](https://github.com/php-curl-class/php-curl-class/pull/748)) + +## 9.12.1 - 2022-12-08 + +- Include option constant that uses the CURLINFO_ prefix ([#745](https://github.com/php-curl-class/php-curl-class/pull/745)) + +## 9.12.0 - 2022-12-07 + +- Add automatic gzip decoding of response ([#744](https://github.com/php-curl-class/php-curl-class/pull/744)) + +## 9.11.1 - 2022-12-06 + +- change: remove unused namespace import ([#743](https://github.com/php-curl-class/php-curl-class/pull/743)) + +## 9.11.0 - 2022-12-05 + +- Add Curl::diagnose() HTTP method check matches methods allowed ([#741](https://github.com/php-curl-class/php-curl-class/pull/741)) +- Add temporary fix missing template params ([#742](https://github.com/php-curl-class/php-curl-class/pull/742)) + +## 9.10.0 - 2022-11-07 + +- Display request options in Curl::diagnose() output ([#739](https://github.com/php-curl-class/php-curl-class/pull/739)) + +## 9.9.0 - 2022-11-06 + +- Fix MultiCurl::setCookieString() ([#738](https://github.com/php-curl-class/php-curl-class/pull/738)) +- Pass MultiCurl options to new Curl instances earlier ([#737](https://github.com/php-curl-class/php-curl-class/pull/737)) +- Add deferred constant curlErrorCodeConstants ([#736](https://github.com/php-curl-class/php-curl-class/pull/736)) + +## 9.8.0 - 2022-10-01 + +- Include curl error code constant in curl error message ([#733](https://github.com/php-curl-class/php-curl-class/pull/733)) + +## 9.7.0 - 2022-09-29 + +- Implement ArrayUtil::arrayRandomIndex() ([#732](https://github.com/php-curl-class/php-curl-class/pull/732)) + +## 9.6.3 - 2022-09-24 + +- Remove filter flag constants deprecated as of PHP 7.3 ([#730](https://github.com/php-curl-class/php-curl-class/pull/730)) + +## 9.6.2 - 2022-09-24 + +- Call MultiCurl::beforeSend() before each request is made ([#723](https://github.com/php-curl-class/php-curl-class/pull/723)) +- Encode keys for post data with numeric keys ([#726](https://github.com/php-curl-class/php-curl-class/pull/726)) +- Fix building post data with object ([#728](https://github.com/php-curl-class/php-curl-class/pull/728)) + +## 9.6.1 - 2022-06-30 + +### Fixed + +- Attempt to stop active requests when `MultiCurl::stop()` is called + [#714](https://github.com/php-curl-class/php-curl-class/issues/714) + [#718](https://github.com/php-curl-class/php-curl-class/issues/718) +- Retain keys for arrays with null values when building post data + [#712](https://github.com/php-curl-class/php-curl-class/issues/712) + +## 9.6.0 - 2022-03-17 + +### Added + +- Method `MultiCurl::stop()` for stopping subsequent requests + [#708](https://github.com/php-curl-class/php-curl-class/issues/708) + +## 9.5.1 - 2021-12-14 + +### Fixed + +- Silence PHP 8.1 deprecations [#691](https://github.com/php-curl-class/php-curl-class/issues/691) +- Remove data parameter from additional request types + [#689](https://github.com/php-curl-class/php-curl-class/issues/689) + +## 9.5.0 - 2021-11-21 + +### Added + +- Method `Curl::setStop()` for stopping requests early without downloading the full response body + [#681](https://github.com/php-curl-class/php-curl-class/issues/681) + +### Fixed + +- Fixed constructing request url when using `MultiCurl::addPost()` + [#686](https://github.com/php-curl-class/php-curl-class/issues/686) + +## 9.4.0 - 2021-09-04 + +### Changed + +- Method `Url::parseUrl()` is now public + +### Fixed + +- Fix parsing schemeless urls [#679](https://github.com/php-curl-class/php-curl-class/issues/679) + +## 9.3.1 - 2021-08-05 + +### Changed + +- Enabled strict types (`declare(strict_types=1);`) + +### Fixed + +- Fixed `Curl::downloadFileName` not being set correctly + +## 9.3.0 - 2021-07-23 + +### Added + +- Method `Curl::diagnose()` for troubleshooting requests + +## 9.2.0 - 2021-06-23 + +### Added + +- Additional Curl::set\* and MultiCurl::set\* helper methods + + ``` + Curl::setAutoReferer() + Curl::setAutoReferrer() + Curl::setFollowLocation() + Curl::setForbidReuse() + Curl::setMaximumRedirects() + MultiCurl::setAutoReferer() + MultiCurl::setAutoReferrer() + MultiCurl::setFollowLocation() + MultiCurl::setForbidReuse() + MultiCurl::setMaximumRedirects() + ``` + +### Fixed + +- Closing curl handles [#670](https://github.com/php-curl-class/php-curl-class/issues/670) +- Use of "$this" in non-object context [#671](https://github.com/php-curl-class/php-curl-class/pull/671) + +## 9.1.0 - 2021-03-24 + +### Added + +- Support for using relative urls with MultiCurl::add\*() methods [#628](https://github.com/php-curl-class/php-curl-class/issues/628) + +## 9.0.0 - 2021-03-19 + +### Changed + +- Use short array syntax + +### Removed + +- Support for PHP 5.3, 5.4, 5.5, and 5.6 [#380](https://github.com/php-curl-class/php-curl-class/issues/380) + +## Manual Review + +A manual review of changes is possible using the +[comparison page](https://github.com/php-curl-class/php-curl-class/compare/). For example, visit +[7.4.0...8.0.0](https://github.com/php-curl-class/php-curl-class/compare/7.4.0...8.0.0) to compare the changes for +the `MAJOR` upgrade from 7.4.0 to 8.0.0. Comparing against `HEAD` is also possible using the `tag...HEAD` syntax +([8.3.0...HEAD](https://github.com/php-curl-class/php-curl-class/compare/8.3.0...HEAD)). + +View the log between releases: + + $ git fetch --tags + $ git log 7.4.0...8.0.0 + +View the code changes between releases: + + $ git fetch --tags + $ git diff 7.4.0...8.0.0 + +View only the source log and code changes between releases: + + $ git log 7.4.0...8.0.0 "src/" + $ git diff 7.4.0...8.0.0 "src/" + +View only the source log and code changes between a release and the current checked-out commit: + + $ git log 8.0.0...head "src/" + $ git diff 8.0.0...head "src/" diff --git a/web/app/vendor/php-curl-class/php-curl-class/README.md b/web/app/vendor/php-curl-class/php-curl-class/README.md index 5934471..0e3b25d 100644 --- a/web/app/vendor/php-curl-class/php-curl-class/README.md +++ b/web/app/vendor/php-curl-class/php-curl-class/README.md @@ -1,101 +1,153 @@ -# php-curl-class +# PHP Curl Class: HTTP requests made easy -[![Build Status](https://travis-ci.org/php-curl-class/php-curl-class.png?branch=master)](https://travis-ci.org/php-curl-class/php-curl-class) +[![](https://img.shields.io/github/release/php-curl-class/php-curl-class.svg?style=flat-square&sort=semver)](https://github.com/php-curl-class/php-curl-class/releases/) +[![](https://img.shields.io/github/license/php-curl-class/php-curl-class.svg?style=flat-square)](https://github.com/php-curl-class/php-curl-class/blob/master/LICENSE) +[![](https://img.shields.io/github/actions/workflow/status/php-curl-class/php-curl-class/ci.yml?style=flat-square&label=build&branch=master)](https://github.com/php-curl-class/php-curl-class/actions/workflows/ci.yml) +[![](https://img.shields.io/github/actions/workflow/status/php-curl-class/php-curl-class/release.yml?style=flat-square&label=release&branch=master)](https://github.com/php-curl-class/php-curl-class/releases/) +[![](https://img.shields.io/packagist/dt/php-curl-class/php-curl-class.svg?style=flat-square)](https://github.com/php-curl-class/php-curl-class/releases/) -PHP Curl Class is an object-oriented wrapper of the PHP cURL extension. +PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs. -### Composer +![PHP Curl Class screencast](www/img/screencast.gif) - $ composer require php-curl-class/php-curl-class +--- + +- [Installation](#installation) +- [Requirements](#requirements) +- [Quick Start and Examples](#quick-start-and-examples) +- [Available Methods](#available-methods) +- [Security](#security) +- [Troubleshooting](#troubleshooting) +- [Testing](#testing) +- [Contributing](#contributing) + +--- + +### Installation + +To install PHP Curl Class, run the following command: + + composer require php-curl-class/php-curl-class + +To install the latest commit version: + + composer require php-curl-class/php-curl-class @dev + +Installation instructions to use the `composer` command can be found on https://github.com/composer/composer. + +### Requirements + +PHP Curl Class works with PHP 7.0, 7.1, 7.2, 7.3, 7.4, 8.0, 8.1, and 8.2. ### Quick Start and Examples +More examples are available under [/examples](https://github.com/php-curl-class/php-curl-class/tree/master/examples). + ```php -require 'Curl.class.php'; +require __DIR__ . '/vendor/autoload.php'; + +use Curl\Curl; $curl = new Curl(); -$curl->get('http://www.example.com/'); +$curl->get('https://www.example.com/'); + +if ($curl->error) { + echo 'Error: ' . $curl->errorMessage . "\n"; + $curl->diagnose(); +} else { + echo 'Response:' . "\n"; + var_dump($curl->response); +} ``` ```php +// https://www.example.com/search?q=keyword $curl = new Curl(); -$curl->get('http://www.example.com/search', array( +$curl->get('https://www.example.com/search', [ 'q' => 'keyword', -)); +]); ``` ```php $curl = new Curl(); -$curl->post('http://www.example.com/login/', array( +$curl->post('https://www.example.com/login/', [ 'username' => 'myusername', 'password' => 'mypassword', -)); +]); ``` ```php $curl = new Curl(); $curl->setBasicAuthentication('username', 'password'); -$curl->setUserAgent(''); -$curl->setReferrer(''); +$curl->setUserAgent('MyUserAgent/0.0.1 (+https://www.example.com/bot.html)'); +$curl->setReferrer('https://www.example.com/url?url=https%3A%2F%2Fwww.example.com%2F'); $curl->setHeader('X-Requested-With', 'XMLHttpRequest'); $curl->setCookie('key', 'value'); -$curl->get('http://www.example.com/'); +$curl->get('https://www.example.com/'); if ($curl->error) { - echo $curl->error_code; -} -else { - echo $curl->response; + echo 'Error: ' . $curl->errorMessage . "\n"; +} else { + echo 'Response:' . "\n"; + var_dump($curl->response); } -var_dump($curl->request_headers); -var_dump($curl->response_headers); +var_dump($curl->requestHeaders); +var_dump($curl->responseHeaders); ``` ```php $curl = new Curl(); -$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false); -$curl->get('https://encrypted.example.com/'); +$curl->setFollowLocation(); +$curl->get('https://shortn.example.com/bHbVsP'); ``` ```php $curl = new Curl(); -$curl->put('http://api.example.com/user/', array( +$curl->put('https://api.example.com/user/', [ 'first_name' => 'Zach', 'last_name' => 'Borboa', -)); +]); ``` ```php $curl = new Curl(); -$curl->patch('http://api.example.com/profile/', array( +$curl->patch('https://api.example.com/profile/', [ 'image' => '@path/to/file.jpg', -)); +]); ``` ```php $curl = new Curl(); -$curl->delete('http://api.example.com/user/', array( +$curl->patch('https://api.example.com/profile/', [ + 'image' => new CURLFile('path/to/file.jpg'), +]); +``` + +```php +$curl = new Curl(); +$curl->delete('https://api.example.com/user/', [ 'id' => '1234', -)); +]); ``` ```php -// Enable gzip compression. +// Enable all supported encoding types and download a file. $curl = new Curl(); -$curl->setOpt(CURLOPT_ENCODING , 'gzip'); -$curl->get('https://www.example.com/image.png'); +$curl->setOpt(CURLOPT_ENCODING , ''); +$curl->download('https://www.example.com/file.bin', '/tmp/myfile.bin'); ``` ```php // Case-insensitive access to headers. $curl = new Curl(); -$curl->get('https://www.example.com/image.png'); -echo $curl->response_headers['Content-Type'] . "\n"; // image/png -echo $curl->response_headers['CoNTeNT-TyPE'] . "\n"; // image/png +$curl->download('https://www.example.com/image.png', '/tmp/myimage.png'); +echo $curl->responseHeaders['Content-Type'] . "\n"; // image/png +echo $curl->responseHeaders['CoNTeNT-TyPE'] . "\n"; // image/png ``` ```php +// Manual clean up. $curl->close(); ``` @@ -106,31 +158,233 @@ curl_close($curl->curl); ``` ```php -// Requests in parallel with callback functions. -$curl = new Curl(); -$curl->setOpt(CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1'); +require __DIR__ . '/vendor/autoload.php'; -$curl->success(function($instance) { - echo 'call was successful. response was' . "\n"; - echo $instance->response . "\n"; +use Curl\MultiCurl; + +// Requests in parallel with callback functions. +$multi_curl = new MultiCurl(); + +$multi_curl->success(function($instance) { + echo 'call to "' . $instance->url . '" was successful.' . "\n"; + echo 'response:' . "\n"; + var_dump($instance->response); }); -$curl->error(function($instance) { - echo 'call was unsuccessful.' . "\n"; - echo 'error code:' . $instance->error_code . "\n"; - echo 'error message:' . $instance->error_message . "\n"; +$multi_curl->error(function($instance) { + echo 'call to "' . $instance->url . '" was unsuccessful.' . "\n"; + echo 'error code: ' . $instance->errorCode . "\n"; + echo 'error message: ' . $instance->errorMessage . "\n"; }); -$curl->complete(function($instance) { +$multi_curl->complete(function($instance) { echo 'call completed' . "\n"; }); -$curl->get(array( - 'https://duckduckgo.com/', - 'https://search.yahoo.com/search', - 'https://www.bing.com/search', - 'http://www.dogpile.com/search/web', - 'https://www.google.com/search', - 'https://www.wolframalpha.com/input/', -), array( +$multi_curl->addGet('https://www.google.com/search', [ 'q' => 'hello world', -)); +]); +$multi_curl->addGet('https://duckduckgo.com/', [ + 'q' => 'hello world', +]); +$multi_curl->addGet('https://www.bing.com/search', [ + 'q' => 'hello world', +]); + +$multi_curl->start(); // Blocks until all items in the queue have been processed. ``` + +More examples are available under [/examples](https://github.com/php-curl-class/php-curl-class/tree/master/examples). + +### Available Methods +```php +Curl::__construct($base_url = null, $options = []) +Curl::__destruct() +Curl::__get($name) +Curl::_fastDownload($url, $filename, $connections = 4) { +Curl::attemptRetry() +Curl::beforeSend($callback) +Curl::buildPostData($data) +Curl::call() +Curl::close() +Curl::complete($callback) +Curl::delete($url, $query_parameters = [], $data = []) +Curl::diagnose($return = false) +Curl::disableTimeout() +Curl::download($url, $mixed_filename) +Curl::error($callback) +Curl::exec($ch = null) +Curl::execDone() +Curl::get($url, $data = []) +Curl::getAttempts() +Curl::getBeforeSendCallback() +Curl::getCompleteCallback() +Curl::getCookie($key) +Curl::getCurl() +Curl::getCurlErrorCode() +Curl::getCurlErrorMessage() +Curl::getDownloadCompleteCallback() +Curl::getDownloadFileName() +Curl::getErrorCallback() +Curl::getErrorCode() +Curl::getErrorMessage() +Curl::getFileHandle() +Curl::getHttpErrorMessage() +Curl::getHttpStatusCode() +Curl::getId() +Curl::getInfo($opt = null) +Curl::getJsonDecoder() +Curl::getOpt($option) +Curl::getRawResponse() +Curl::getRawResponseHeaders() +Curl::getRemainingRetries() +Curl::getRequestHeaders() +Curl::getResponse() +Curl::getResponseCookie($key) +Curl::getResponseCookies() +Curl::getResponseHeaders() +Curl::getRetries() +Curl::getRetryDecider() +Curl::getSuccessCallback() +Curl::getUrl() +Curl::getXmlDecoder() +Curl::head($url, $data = []) +Curl::isChildOfMultiCurl() +Curl::isCurlError() +Curl::isError() +Curl::isHttpError() +Curl::options($url, $data = []) +Curl::patch($url, $data = []) +Curl::post($url, $data = '', $follow_303_with_post = false) +Curl::progress($callback) +Curl::put($url, $data = []) +Curl::removeHeader($key) +Curl::reset() +Curl::search($url, $data = []) +Curl::setAutoReferer($auto_referer = true) +Curl::setAutoReferrer($auto_referrer = true) +Curl::setBasicAuthentication($username, $password = '') +Curl::setConnectTimeout($seconds) +Curl::setCookie($key, $value) +Curl::setCookieFile($cookie_file) +Curl::setCookieJar($cookie_jar) +Curl::setCookieString($string) +Curl::setCookies($cookies) +Curl::setDefaultDecoder($mixed = 'json') +Curl::setDefaultHeaderOut() +Curl::setDefaultJsonDecoder() +Curl::setDefaultTimeout() +Curl::setDefaultUserAgent() +Curl::setDefaultXmlDecoder() +Curl::setDigestAuthentication($username, $password = '') +Curl::setFile($file) +Curl::setFollowLocation($follow_location = true) +Curl::setForbidReuse($forbid_reuse = true) +Curl::setHeader($key, $value) +Curl::setHeaders($headers) +Curl::setInterface($interface) +Curl::setJsonDecoder($mixed) +Curl::setMaxFilesize($bytes) +Curl::setMaximumRedirects($maximum_redirects) +Curl::setOpt($option, $value) +Curl::setOpts($options) +Curl::setPort($port) +Curl::setProxy($proxy, $port = null, $username = null, $password = null) +Curl::setProxyAuth($auth) +Curl::setProxyTunnel($tunnel = true) +Curl::setProxyType($type) +Curl::setRange($range) +Curl::setReferer($referer) +Curl::setReferrer($referrer) +Curl::setRetry($mixed) +Curl::setStop($callback = null) +Curl::setTimeout($seconds) +Curl::setUrl($url, $mixed_data = '') +Curl::setUserAgent($user_agent) +Curl::setXmlDecoder($mixed) +Curl::stop() +Curl::success($callback) +Curl::unsetHeader($key) +Curl::unsetProxy() +Curl::verbose($on = true, $output = 'STDERR') +MultiCurl::__construct($base_url = null) +MultiCurl::__destruct() +MultiCurl::addCurl(Curl $curl) +MultiCurl::addDelete($url, $query_parameters = [], $data = []) +MultiCurl::addDownload($url, $mixed_filename) +MultiCurl::addGet($url, $data = []) +MultiCurl::addHead($url, $data = []) +MultiCurl::addOptions($url, $data = []) +MultiCurl::addPatch($url, $data = []) +MultiCurl::addPost($url, $data = '', $follow_303_with_post = false) +MultiCurl::addPut($url, $data = []) +MultiCurl::addSearch($url, $data = []) +MultiCurl::beforeSend($callback) +MultiCurl::close() +MultiCurl::complete($callback) +MultiCurl::disableTimeout() +MultiCurl::error($callback) +MultiCurl::getOpt($option) +MultiCurl::removeHeader($key) +MultiCurl::setAutoReferer($auto_referer = true) +MultiCurl::setAutoReferrer($auto_referrer = true) +MultiCurl::setBasicAuthentication($username, $password = '') +MultiCurl::setConcurrency($concurrency) +MultiCurl::setConnectTimeout($seconds) +MultiCurl::setCookie($key, $value) +MultiCurl::setCookieFile($cookie_file) +MultiCurl::setCookieJar($cookie_jar) +MultiCurl::setCookieString($string) +MultiCurl::setCookies($cookies) +MultiCurl::setDigestAuthentication($username, $password = '') +MultiCurl::setFile($file) +MultiCurl::setFollowLocation($follow_location = true) +MultiCurl::setForbidReuse($forbid_reuse = true) +MultiCurl::setHeader($key, $value) +MultiCurl::setHeaders($headers) +MultiCurl::setInterface($interface) +MultiCurl::setJsonDecoder($mixed) +MultiCurl::setMaximumRedirects($maximum_redirects) +MultiCurl::setOpt($option, $value) +MultiCurl::setOpts($options) +MultiCurl::setPort($port) +MultiCurl::setProxies($proxies) +MultiCurl::setProxy($proxy, $port = null, $username = null, $password = null) +MultiCurl::setProxyAuth($auth) +MultiCurl::setProxyTunnel($tunnel = true) +MultiCurl::setProxyType($type) +MultiCurl::setRange($range) +MultiCurl::setRateLimit($rate_limit) +MultiCurl::setReferer($referer) +MultiCurl::setReferrer($referrer) +MultiCurl::setRequestTimeAccuracy() +MultiCurl::setRetry($mixed) +MultiCurl::setTimeout($seconds) +MultiCurl::setUrl($url, $mixed_data = '') +MultiCurl::setUserAgent($user_agent) +MultiCurl::setXmlDecoder($mixed) +MultiCurl::start() +MultiCurl::stop() +MultiCurl::success($callback) +MultiCurl::unsetHeader($key) +MultiCurl::unsetProxy() +MultiCurl::verbose($on = true, $output = 'STDERR') +``` + +### Security + +See [SECURITY](https://github.com/php-curl-class/php-curl-class/blob/master/SECURITY.md) for security considerations. + +### Troubleshooting + +See [TROUBLESHOOTING](https://github.com/php-curl-class/php-curl-class/blob/master/TROUBLESHOOTING.md) for help troubleshooting. + +### Testing + +See [TESTING](https://github.com/php-curl-class/php-curl-class/blob/master/TESTING.md) for testing information. + +### Contributing + +1. Check for open issues or open a new issue to start a discussion around a bug or feature. +1. Fork the repository on GitHub to start making your changes. +1. Write one or more tests for the new feature or that expose the bug. +1. Make code changes to implement the feature or fix the bug. +1. Send a pull request to get your changes merged and published. diff --git a/web/app/vendor/php-curl-class/php-curl-class/SECURITY.md b/web/app/vendor/php-curl-class/php-curl-class/SECURITY.md new file mode 100644 index 0000000..66cca46 --- /dev/null +++ b/web/app/vendor/php-curl-class/php-curl-class/SECURITY.md @@ -0,0 +1,108 @@ +# Security Considerations + +### Url may point to system files + +* Don't blindly accept urls from users as they may point to system files. Curl supports many protocols including `FILE`. + The following would show the contents of `file:///etc/passwd`. + +```bash +# Attacker. +$ curl https://www.example.com/display_webpage.php?url=file%3A%2F%2F%2Fetc%2Fpasswd +``` + +```php +// display_webpage.php +$url = $_GET['url']; // DANGER! +$curl = new Curl(); +$curl->get($url); +echo $curl->response; +``` + +Safer: + +```php +function is_allowed_url($url, $allowed_url_schemes = ['http', 'https']) { + $valid_url = filter_var($url, FILTER_VALIDATE_URL) !== false; + if ($valid_url) { + $scheme = parse_url($url, PHP_URL_SCHEME); + return in_array($scheme, $allowed_url_schemes, true); + } + $valid_ip = filter_var($url, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false; + return $valid_ip; +} + +$url = $_GET['url']; +if (!is_allowed_url($url)) { + die('Unsafe url detected.'); +} +``` + +### Url may point to internal urls + +* Url may point to internal urls including those behind a firewall (e.g. http://192.168.0.1/ or ftp://192.168.0.1/). Use + a whitelist to allow certain urls rather than a blacklist. + +### Request data may refer to system files + +* Request data prefixed with the `@` character may have special interpretation and read from system files. + +```bash +# Attacker. +$ curl https://www.example.com/upload_photo.php --data "photo=@/etc/passwd" +``` + +```php +// upload_photo.php +$curl = new Curl(); +$curl->post('http://www.anotherwebsite.com/', [ + 'photo' => $_POST['photo'], // DANGER! +]); +``` + +### Unsafe response with redirection enabled + +* Requests with redirection enabled may return responses from unexpected sources. + Downloading https://www.example.com/image.png may redirect and download https://www.evil.com/virus.exe + +```php +$curl = new Curl(); +$curl->setOpt(CURLOPT_FOLLOWLOCATION, true); // DANGER! +$curl->download('https://www.example.com/image.png', 'my_image.png'); +``` + +```php +$curl = new Curl(); +$curl->setOpt(CURLOPT_FOLLOWLOCATION, true); // DANGER! +$curl->get('https://www.example.com/image.png'); +``` + +### Keep SSL protections enabled + +* Do not disable SSL protections. + +```php +curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // DANGER! +curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // DANGER! +``` + +### Prevent XML External Entity injection + +* Set the following when using the default PHP XML parser to prevent XML external entity injection. + +```php +libxml_disable_entity_loader(true); +``` + +### Prevent PHP execution of library files + +PHP files in this library are not intended to be accessible by users browsing websites. Prevent direct access to library files by moving the library folder at least one level higher than the web root directory. Alternatively, configure the server to disable php file execution for all library files. + +#### For WordPress plugin developers + +WordPress plugin developers that wish to incorporate the PHP Curl Class library into their plugin, should take special care to include only the "core" library files. + +Do one of the following: + +Option 1. Download an official release from the [releases page](https://github.com/php-curl-class/php-curl-class/releases) and incorporate the files contained in the compressed file into the plugin. The releases include only the necessary php files for the library to function. + +Option 2. Manually copy only the [src/](https://github.com/php-curl-class/php-curl-class/tree/master/src) directory into your plugin. Be sure not to copy any other php files as they may be executable by users visiting the php files directly. diff --git a/web/app/vendor/php-curl-class/php-curl-class/composer.json b/web/app/vendor/php-curl-class/php-curl-class/composer.json index 007da18..7e6f720 100644 --- a/web/app/vendor/php-curl-class/php-curl-class/composer.json +++ b/web/app/vendor/php-curl-class/php-curl-class/composer.json @@ -1,7 +1,42 @@ { "name": "php-curl-class/php-curl-class", - "description": "PHP Curl Class is an object-oriented wrapper of the PHP cURL extension.", + "description": "PHP Curl Class makes it easy to send HTTP requests and integrate with web APIs.", + "homepage": "https://github.com/php-curl-class/php-curl-class", + "license": "Unlicense", + "keywords": [ + "php", "curl", "class", "api", "api-client", "client", "framework", "http", "http-client", "http-proxy", "json", + "php-curl", "php-curl-library", "proxy", "requests", "restful", "web-scraper", "web-scraping", "web-service", + "xml" + ], + "authors": [ + { + "name": "Zach Borboa" + } + ], + "require": { + "php": ">=7.0", + "ext-curl": "*" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "*", + "ext-gd": "*", + "phpcompatibility/php-compatibility": "dev-develop", + "phpcsstandards/phpcsutils": "@alpha", + "phpunit/phpunit": "*", + "squizlabs/php_codesniffer": "*", + "vimeo/psalm": "*" + }, + "suggest": { + "ext-mbstring": "*" + }, "autoload": { - "classmap": ["src/"] + "psr-4": { + "Curl\\": "src/Curl/" + } + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } } } diff --git a/web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_account_balance.php b/web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_account_balance.php deleted file mode 100644 index b45add8..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_account_balance.php +++ /dev/null @@ -1,22 +0,0 @@ -setHeader('ACCESS_KEY', API_KEY); -$curl->setHeader('ACCESS_SIGNATURE', $signature); -$curl->setHeader('ACCESS_NONCE', $nonce); -$curl->get($url); - -echo - 'My current account balance at Coinbase is ' . - $curl->response->amount . ' ' . $curl->response->currency . '.' . "\n"; diff --git a/web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_spot_rate.php b/web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_spot_rate.php deleted file mode 100644 index f84e450..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/examples/coinbase_spot_rate.php +++ /dev/null @@ -1,10 +0,0 @@ -get('https://coinbase.com/api/v1/prices/spot_rate'); - -echo - 'The current price of bitcoin at Coinbase is ' . - '$' . $curl->response->amount . ' ' . $curl->response->currency . '.' . "\n"; diff --git a/web/app/vendor/php-curl-class/php-curl-class/examples/instagram_search_photos.php b/web/app/vendor/php-curl-class/php-curl-class/examples/instagram_search_photos.php deleted file mode 100644 index dad8d96..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/examples/instagram_search_photos.php +++ /dev/null @@ -1,17 +0,0 @@ -get('https://api.instagram.com/v1/media/search', array( - 'client_id' => INSTAGRAM_CLIENT_ID, - 'lat' => '37.8296', - 'lng' => '-122.4832', -)); - -foreach ($curl->response->data as $media) { - $image = $media->images->low_resolution; - echo ''; -} diff --git a/web/app/vendor/php-curl-class/php-curl-class/examples/put.php b/web/app/vendor/php-curl-class/php-curl-class/examples/put.php deleted file mode 100644 index f06e14c..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/examples/put.php +++ /dev/null @@ -1,14 +0,0 @@ -put('http://httpbin.org/put', array( - 'id' => 1, - 'first_name' => 'Zach', - 'last_name' => 'Borboa', -)); - -echo 'Data server received via PUT:' . "\n"; -var_dump($curl->response->form); diff --git a/web/app/vendor/php-curl-class/php-curl-class/examples/twitter_post_tweet.php b/web/app/vendor/php-curl-class/php-curl-class/examples/twitter_post_tweet.php deleted file mode 100644 index 18b7671..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/examples/twitter_post_tweet.php +++ /dev/null @@ -1,35 +0,0 @@ - API_KEY, - 'oauth_nonce' => md5(microtime() . mt_rand()), - 'oauth_signature_method' => 'HMAC-SHA1', - 'oauth_timestamp' => time(), - 'oauth_token' => OAUTH_ACCESS_TOKEN, - 'oauth_version' => '1.0', - 'status' => $status, -); - -$url = 'https://api.twitter.com/1.1/statuses/update.json'; -$request = implode('&', array( - 'POST', - rawurlencode($url), - rawurlencode(http_build_query($oauth_data, '', '&', PHP_QUERY_RFC3986)), -)); -$key = implode('&', array(API_SECRET, OAUTH_TOKEN_SECRET)); -$oauth_data['oauth_signature'] = base64_encode(hash_hmac('sha1', $request, $key, true)); -$data = http_build_query($oauth_data, '', '&'); - -$curl = new Curl(); -$curl->post($url, $data); - -echo 'Posted "' . $curl->response->text . '" at ' . $curl->response->created_at . '.' . "\n"; diff --git a/web/app/vendor/php-curl-class/php-curl-class/src/Curl.class.php b/web/app/vendor/php-curl-class/php-curl-class/src/Curl.class.php deleted file mode 100644 index 26209ba..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/src/Curl.class.php +++ /dev/null @@ -1,496 +0,0 @@ -curl = curl_init(); - $this->setUserAgent(self::USER_AGENT); - $this->setOpt(CURLINFO_HEADER_OUT, true); - $this->setOpt(CURLOPT_HEADER, true); - $this->setOpt(CURLOPT_RETURNTRANSFER, true); - } - - public function get($url_mixed, $data = array()) - { - if (is_array($url_mixed)) { - $curl_multi = curl_multi_init(); - $this->multi_parent = true; - - $this->curls = array(); - - foreach ($url_mixed as $url) { - $curl = new Curl(); - $curl->multi_child = true; - $curl->setOpt(CURLOPT_URL, $this->buildURL($url, $data), $curl->curl); - $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); - $curl->setOpt(CURLOPT_HTTPGET, true); - $this->call($this->before_send_function, $curl); - $this->curls[] = $curl; - - $curlm_error_code = curl_multi_add_handle($curl_multi, $curl->curl); - if (!($curlm_error_code === CURLM_OK)) { - throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)); - } - } - - foreach ($this->curls as $ch) { - foreach ($this->options as $key => $value) { - $ch->setOpt($key, $value); - } - } - - do { - $status = curl_multi_exec($curl_multi, $active); - } while ($status === CURLM_CALL_MULTI_PERFORM || $active); - - foreach ($this->curls as $ch) { - $this->exec($ch); - } - } else { - $this->setopt(CURLOPT_URL, $this->buildURL($url_mixed, $data)); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); - $this->setopt(CURLOPT_HTTPGET, true); - return $this->exec(); - } - } - - public function post($url, $data = array()) - { - if (is_array($data) && empty($data)) { - $this->setHeader('Content-Length'); - } - - $this->setOpt(CURLOPT_URL, $this->buildURL($url)); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST'); - $this->setOpt(CURLOPT_POST, true); - $this->setOpt(CURLOPT_POSTFIELDS, $this->postfields($data)); - return $this->exec(); - } - - public function put($url, $data = array()) - { - $this->setOpt(CURLOPT_URL, $url); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT'); - $put_data = http_build_query($data); - if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { - $this->setHeader('Content-Length', strlen($put_data)); - } - $this->setOpt(CURLOPT_POSTFIELDS, $put_data); - return $this->exec(); - } - - public function patch($url, $data = array()) - { - $this->setHeader('Content-Length'); - $this->setOpt(CURLOPT_URL, $this->buildURL($url)); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH'); - $this->setOpt(CURLOPT_POSTFIELDS, $data); - return $this->exec(); - } - - public function delete($url, $data = array()) - { - $this->setHeader('Content-Length'); - $this->setOpt(CURLOPT_URL, $this->buildURL($url, $data)); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE'); - return $this->exec(); - } - - public function head($url, $data = array()) - { - $this->setOpt(CURLOPT_URL, $this->buildURL($url, $data)); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD'); - $this->setOpt(CURLOPT_NOBODY, true); - return $this->exec(); - } - - public function options($url, $data = array()) - { - $this->setHeader('Content-Length'); - $this->setOpt(CURLOPT_URL, $this->buildURL($url, $data)); - $this->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS'); - return $this->exec(); - } - - public function setBasicAuthentication($username, $password) - { - $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC); - $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); - } - - public function setHeader($key, $value = '') - { - $this->headers[$key] = $key . ': ' . $value; - $this->setOpt(CURLOPT_HTTPHEADER, array_values($this->headers)); - } - - public function setUserAgent($user_agent) - { - $this->setOpt(CURLOPT_USERAGENT, $user_agent); - } - - public function setReferrer($referrer) - { - $this->setOpt(CURLOPT_REFERER, $referrer); - } - - public function setCookie($key, $value) - { - $this->cookies[$key] = $value; - $this->setOpt(CURLOPT_COOKIE, http_build_query($this->cookies, '', '; ')); - } - - public function setCookieFile($cookie_file) - { - $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file); - } - - public function setCookieJar($cookie_jar) - { - $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar); - } - - public function setOpt($option, $value, $_ch = null) - { - $ch = is_null($_ch) ? $this->curl : $_ch; - - $required_options = array( - CURLINFO_HEADER_OUT => 'CURLINFO_HEADER_OUT', - CURLOPT_HEADER => 'CURLOPT_HEADER', - CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER', - ); - - if (in_array($option, array_keys($required_options), true) && !($value === true)) { - trigger_error($required_options[$option] . ' is a required option', E_USER_WARNING); - } - - $this->options[$option] = $value; - return curl_setopt($ch, $option, $value); - } - - public function verbose($on = true) - { - $this->setOpt(CURLOPT_VERBOSE, $on); - } - - public function close() - { - if ($this->multi_parent) { - foreach ($this->curls as $curl) { - $curl->close(); - } - } - - if (is_resource($this->curl)) { - curl_close($this->curl); - } - } - - public function beforeSend($function) - { - $this->before_send_function = $function; - } - - public function success($callback) - { - $this->success_function = $callback; - } - - public function error($callback) - { - $this->error_function = $callback; - } - - public function complete($callback) - { - $this->complete_function = $callback; - } - - private function buildURL($url, $data = array()) - { - return $url . (empty($data) ? '' : '?' . http_build_query($data)); - } - - private function parseHeaders($raw_headers) - { - $raw_headers = preg_split('/\r\n/', $raw_headers, null, PREG_SPLIT_NO_EMPTY); - $http_headers = new CaseInsensitiveArray(); - - for ($i = 1; $i < count($raw_headers); $i++) { - list($key, $value) = explode(':', $raw_headers[$i], 2); - $key = trim($key); - $value = trim($value); - // Use isset() as array_key_exists() and ArrayAccess are not compatible. - if (isset($http_headers[$key])) { - $http_headers[$key] .= ',' . $value; - } else { - $http_headers[$key] = $value; - } - } - - return array(isset($raw_headers['0']) ? $raw_headers['0'] : '', $http_headers); - } - - private function parseRequestHeaders($raw_headers) - { - $request_headers = new CaseInsensitiveArray(); - list($first_line, $headers) = $this->parseHeaders($raw_headers); - $request_headers['Request-Line'] = $first_line; - foreach ($headers as $key => $value) { - $request_headers[$key] = $value; - } - return $request_headers; - } - - private function parseResponseHeaders($raw_headers) - { - $response_headers = new CaseInsensitiveArray(); - list($first_line, $headers) = $this->parseHeaders($raw_headers); - $response_headers['Status-Line'] = $first_line; - foreach ($headers as $key => $value) { - $response_headers[$key] = $value; - } - return $response_headers; - } - - private function postfields($data) - { - if (is_array($data)) { - if (is_array_multidim($data)) { - $data = http_build_multi_query($data); - } else { - foreach ($data as $key => $value) { - // Fix "Notice: Array to string conversion" when $value in - // curl_setopt($ch, CURLOPT_POSTFIELDS, $value) is an array - // that contains an empty array. - if (is_array($value) && empty($value)) { - $data[$key] = ''; - // Fix "curl_setopt(): The usage of the @filename API for - // file uploading is deprecated. Please use the CURLFile - // class instead". - } elseif (is_string($value) && strpos($value, '@') === 0) { - if (class_exists('CURLFile')) { - $data[$key] = new CURLFile(substr($value, 1)); - } - } - } - } - } - - return $data; - } - - protected function exec($_ch = null) - { - $ch = is_null($_ch) ? $this : $_ch; - - if ($ch->multi_child) { - $ch->response = curl_multi_getcontent($ch->curl); - } else { - $ch->response = curl_exec($ch->curl); - } - - $ch->curl_error_code = curl_errno($ch->curl); - $ch->curl_error_message = curl_error($ch->curl); - $ch->curl_error = !($ch->curl_error_code === 0); - $ch->http_status_code = curl_getinfo($ch->curl, CURLINFO_HTTP_CODE); - $ch->http_error = in_array(floor($ch->http_status_code / 100), array(4, 5)); - $ch->error = $ch->curl_error || $ch->http_error; - $ch->error_code = $ch->error ? ($ch->curl_error ? $ch->curl_error_code : $ch->http_status_code) : 0; - - $ch->request_headers = $this->parseRequestHeaders(curl_getinfo($ch->curl, CURLINFO_HEADER_OUT)); - $ch->response_headers = ''; - if (!(strpos($ch->response, "\r\n\r\n") === false)) { - list($response_header, $ch->response) = explode("\r\n\r\n", $ch->response, 2); - if ($response_header === 'HTTP/1.1 100 Continue') { - list($response_header, $ch->response) = explode("\r\n\r\n", $ch->response, 2); - } - $ch->response_headers = $this->parseResponseHeaders($response_header); - - if (isset($ch->response_headers['Content-Type'])) { - if (preg_match('/^application\/json/i', $ch->response_headers['Content-Type'])) { - $json_obj = json_decode($ch->response, false); - if (!is_null($json_obj)) { - $ch->response = $json_obj; - } - } - } - } - - $ch->http_error_message = ''; - if ($ch->error) { - if (isset($ch->response_headers['Status-Line'])) { - $ch->http_error_message = $ch->response_headers['Status-Line']; - } - } - $ch->error_message = $ch->curl_error ? $ch->curl_error_message : $ch->http_error_message; - - if (!$ch->error) { - $ch->call($this->success_function, $ch); - } else { - $ch->call($this->error_function, $ch); - } - - $ch->call($this->complete_function, $ch); - - return $ch->error_code; - } - - private function call($function) - { - if (is_callable($function)) { - $args = func_get_args(); - array_shift($args); - call_user_func_array($function, $args); - } - } - - public function __destruct() - { - $this->close(); - } -} - -class CaseInsensitiveArray implements ArrayAccess, Countable, Iterator -{ - private $container = array(); - - public function offsetSet($offset, $value) - { - if (is_null($offset)) { - $this->container[] = $value; - } else { - $index = array_search(strtolower($offset), array_keys(array_change_key_case($this->container, CASE_LOWER))); - if (!($index === false)) { - $keys = array_keys($this->container); - unset($this->container[$keys[$index]]); - } - $this->container[$offset] = $value; - } - } - - public function offsetExists($offset) - { - return array_key_exists(strtolower($offset), array_change_key_case($this->container, CASE_LOWER)); - } - - public function offsetUnset($offset) - { - unset($this->container[$offset]); - } - - public function offsetGet($offset) - { - $index = array_search(strtolower($offset), array_keys(array_change_key_case($this->container, CASE_LOWER))); - if ($index === false) { - return null; - } - - $values = array_values($this->container); - return $values[$index]; - } - - public function count() - { - return count($this->container); - } - - public function current() - { - return current($this->container); - } - - public function next() - { - return next($this->container); - } - - public function key() - { - return key($this->container); - } - - public function valid() - { - return !($this->current() === false); - } - - public function rewind() - { - reset($this->container); - } -} - -function is_array_assoc($array) -{ - return (bool)count(array_filter(array_keys($array), 'is_string')); -} - -function is_array_multidim($array) -{ - if (!is_array($array)) { - return false; - } - - return !(count($array) === count($array, COUNT_RECURSIVE)); -} - -function http_build_multi_query($data, $key = null) -{ - $query = array(); - - if (empty($data)) { - return $key . '='; - } - - $is_array_assoc = is_array_assoc($data); - - foreach ($data as $k => $value) { - if (is_string($value) || is_numeric($value)) { - $brackets = $is_array_assoc ? '[' . $k . ']' : '[]'; - $query[] = urlencode(is_null($key) ? $k : $key . $brackets) . '=' . rawurlencode($value); - } elseif (is_array($value)) { - $nested = is_null($key) ? $k : $key . '[' . $k . ']'; - $query[] = http_build_multi_query($value, $nested); - } - } - - return implode('&', $query); -} diff --git a/web/app/vendor/php-curl-class/php-curl-class/src/Curl/ArrayUtil.php b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/ArrayUtil.php new file mode 100644 index 0000000..7c264a2 --- /dev/null +++ b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/ArrayUtil.php @@ -0,0 +1,170 @@ + $value) { + if (is_scalar($value)) { + if ($prefix) { + $return[$prefix . '[' . $key . ']'] = $value; + } else { + $return[$key] = $value; + } + } else { + if ($value instanceof \CURLFile) { + $return[$key] = $value; + } elseif ($value instanceof \CURLStringFile) { + $return[$key] = $value; + } else { + $return = array_merge( + $return, + self::arrayFlattenMultidim( + $value, + $prefix ? $prefix . '[' . $key . ']' : $key + ) + ); + } + } + } + } + } elseif ($array === null) { + $return[$prefix] = $array; + } + return $return; + } + + /** + * Array Flatten Multidim + * + * @deprecated Use ArrayUtil::arrayFlattenMultidim(). + * @access public + * @param $array + * @param $prefix + * + * @return array + */ + public static function array_flatten_multidim($array, $prefix = false) + { + return static::arrayFlattenMultidim($array, $prefix); + } + + /** + * Array Random + * + * @access public + * @param $array + * + * @return mixed + */ + public static function arrayRandom($array) + { + return $array[static::arrayRandomIndex($array)]; + } + + /** + * Array Random Index + * + * @access public + * @param $array + * + * @return integer + */ + public static function arrayRandomIndex($array) + { + return mt_rand(0, count($array) - 1); + } + + /** + * Array Random + * + * @deprecated Use ArrayUtil::arrayRandom(). + * @access public + * @param $array + * + * @return mixed + */ + public static function array_random($array) + { + return static::arrayRandom($array); + } +} diff --git a/web/app/vendor/php-curl-class/php-curl-class/src/Curl/BaseCurl.php b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/BaseCurl.php new file mode 100644 index 0000000..1514ca6 --- /dev/null +++ b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/BaseCurl.php @@ -0,0 +1,395 @@ +beforeSendCallback = $callback; + } + + abstract public function close(); + + /** + * Complete + * + * @access public + * @param $callback callable|null + */ + public function complete($callback) + { + $this->completeCallback = $callback; + } + + /** + * Disable Timeout + * + * @access public + */ + public function disableTimeout() + { + $this->setTimeout(null); + } + + /** + * Error + * + * @access public + * @param $callback callable|null + */ + public function error($callback) + { + $this->errorCallback = $callback; + } + + /** + * Get Opt + * + * @access public + * @param $option + * + * @return mixed + */ + public function getOpt($option) + { + return isset($this->options[$option]) ? $this->options[$option] : null; + } + + /** + * Remove Header + * + * Remove an internal header from the request. + * Using `curl -H "Host:" ...' is equivalent to $curl->removeHeader('Host');. + * + * @access public + * @param $key + */ + public function removeHeader($key) + { + $this->setHeader($key, ''); + } + + /** + * Set auto referer + * + * @access public + */ + public function setAutoReferer($auto_referer = true) + { + $this->setAutoReferrer($auto_referer); + } + + /** + * Set auto referrer + * + * @access public + */ + public function setAutoReferrer($auto_referrer = true) + { + $this->setOpt(CURLOPT_AUTOREFERER, $auto_referrer); + } + + /** + * Set Basic Authentication + * + * @access public + * @param $username + * @param $password + */ + public function setBasicAuthentication($username, $password = '') + { + $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); + } + + /** + * Set Connect Timeout + * + * @access public + * @param $seconds + */ + public function setConnectTimeout($seconds) + { + $this->setOpt(CURLOPT_CONNECTTIMEOUT, $seconds); + } + + abstract public function setCookie($key, $value); + abstract public function setCookieFile($cookie_file); + abstract public function setCookieJar($cookie_jar); + abstract public function setCookieString($string); + abstract public function setCookies($cookies); + + /** + * Set Digest Authentication + * + * @access public + * @param $username + * @param $password + */ + public function setDigestAuthentication($username, $password = '') + { + $this->setOpt(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + $this->setOpt(CURLOPT_USERPWD, $username . ':' . $password); + } + + /** + * Set File + * + * @access public + * @param $file + */ + public function setFile($file) + { + $this->setOpt(CURLOPT_FILE, $file); + } + + /** + * Set follow location + * + * @access public + */ + public function setFollowLocation($follow_location = true) + { + $this->setOpt(CURLOPT_FOLLOWLOCATION, $follow_location); + } + + /** + * Set forbid reuse + * + * @access public + */ + public function setForbidReuse($forbid_reuse = true) + { + $this->setOpt(CURLOPT_FORBID_REUSE, $forbid_reuse); + } + + abstract public function setHeader($key, $value); + abstract public function setHeaders($headers); + + /** + * Set Interface + * + * The name of the outgoing network interface to use. + * This can be an interface name, an IP address or a host name. + * + * @access public + * @param $interface + */ + public function setInterface($interface) + { + $this->setOpt(CURLOPT_INTERFACE, $interface); + } + + abstract public function setJsonDecoder($mixed); + + /** + * Set maximum redirects + * + * @access public + */ + public function setMaximumRedirects($maximum_redirects) + { + $this->setOpt(CURLOPT_MAXREDIRS, $maximum_redirects); + } + + abstract public function setOpt($option, $value); + abstract public function setOpts($options); + + /** + * Set Port + * + * @access public + * @param $port + */ + public function setPort($port) + { + $this->setOpt(CURLOPT_PORT, (int) $port); + } + + /** + * Set Proxy + * + * Set an HTTP proxy to tunnel requests through. + * + * @access public + * @param $proxy - The HTTP proxy to tunnel requests through. May include port number. + * @param $port - The port number of the proxy to connect to. This port number can also be set in $proxy. + * @param $username - The username to use for the connection to the proxy. + * @param $password - The password to use for the connection to the proxy. + */ + public function setProxy($proxy, $port = null, $username = null, $password = null) + { + $this->setOpt(CURLOPT_PROXY, $proxy); + if ($port !== null) { + $this->setOpt(CURLOPT_PROXYPORT, $port); + } + if ($username !== null && $password !== null) { + $this->setOpt(CURLOPT_PROXYUSERPWD, $username . ':' . $password); + } + } + + /** + * Set Proxy Auth + * + * Set the HTTP authentication method(s) to use for the proxy connection. + * + * @access public + * @param $auth + */ + public function setProxyAuth($auth) + { + $this->setOpt(CURLOPT_PROXYAUTH, $auth); + } + + /** + * Set Proxy Tunnel + * + * Set the proxy to tunnel through HTTP proxy. + * + * @access public + * @param $tunnel boolean + */ + public function setProxyTunnel($tunnel = true) + { + $this->setOpt(CURLOPT_HTTPPROXYTUNNEL, $tunnel); + } + + /** + * Set Proxy Type + * + * Set the proxy protocol type. + * + * @access public + * @param $type + */ + public function setProxyType($type) + { + $this->setOpt(CURLOPT_PROXYTYPE, $type); + } + + /** + * Set Range + * + * @access public + * @param $range + */ + public function setRange($range) + { + $this->setOpt(CURLOPT_RANGE, $range); + } + + /** + * Set Referer + * + * @access public + * @param $referer + */ + public function setReferer($referer) + { + $this->setReferrer($referer); + } + + /** + * Set Referrer + * + * @access public + * @param $referrer + */ + public function setReferrer($referrer) + { + $this->setOpt(CURLOPT_REFERER, $referrer); + } + + abstract public function setRetry($mixed); + + /** + * Set Timeout + * + * @access public + * @param $seconds + */ + public function setTimeout($seconds) + { + $this->setOpt(CURLOPT_TIMEOUT, $seconds); + } + + abstract public function setUrl($url, $mixed_data = ''); + + /** + * Set User Agent + * + * @access public + * @param $user_agent + */ + public function setUserAgent($user_agent) + { + $this->setOpt(CURLOPT_USERAGENT, $user_agent); + } + + abstract public function setXmlDecoder($mixed); + abstract public function stop(); + + /** + * Success + * + * @access public + * @param $callback callable|null + */ + public function success($callback) + { + $this->successCallback = $callback; + } + + abstract public function unsetHeader($key); + + /** + * Unset Proxy + * + * Disable use of the proxy. + * + * @access public + */ + public function unsetProxy() + { + $this->setOpt(CURLOPT_PROXY, null); + } + + /** + * Verbose + * + * @access public + * @param bool $on + * @param resource|string $output + */ + public function verbose($on = true, $output = 'STDERR') + { + if ($output === 'STDERR') { + if (!defined('STDERR')) { + define('STDERR', fopen('php://stderr', 'wb')); + } + $output = STDERR; + } + + // Turn off CURLINFO_HEADER_OUT for verbose to work. This has the side + // effect of causing Curl::requestHeaders to be empty. + if ($on) { + $this->setOpt(CURLINFO_HEADER_OUT, false); + } + $this->setOpt(CURLOPT_VERBOSE, $on); + $this->setOpt(CURLOPT_STDERR, $output); + } +} diff --git a/web/app/vendor/php-curl-class/php-curl-class/src/Curl/CaseInsensitiveArray.php b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/CaseInsensitiveArray.php new file mode 100644 index 0000000..13fb015 --- /dev/null +++ b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/CaseInsensitiveArray.php @@ -0,0 +1,245 @@ + $value) { + $this->offsetSet($key, $value); + } + } + } + + /** + * Offset Set + * + * Set data at a specified offset. Converts the offset to lowercase, and + * stores the case-sensitive offset and the data at the lowercase indexes in + * $this->keys and @this->data. + * + * @see https://secure.php.net/manual/en/arrayaccess.offsetset.php + * + * @param string $offset The offset to store the data at (case-insensitive). + * @param mixed $value The data to store at the specified offset. + * + * @return void + * + * @access public + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) + { + if ($offset === null) { + $this->data[] = $value; + } else { + $offsetlower = strtolower($offset); + $this->data[$offsetlower] = $value; + $this->keys[$offsetlower] = $offset; + } + } + + /** + * Offset Exists + * + * Checks if the offset exists in data storage. The index is looked up with + * the lowercase version of the provided offset. + * + * @see https://secure.php.net/manual/en/arrayaccess.offsetexists.php + * + * @param string $offset Offset to check + * + * @return bool If the offset exists. + * + * @access public + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) + { + return (bool) array_key_exists(strtolower($offset), $this->data); + } + + /** + * Offset Unset + * + * Unsets the specified offset. Converts the provided offset to lowercase, + * and unsets the case-sensitive key, as well as the stored data. + * + * @see https://secure.php.net/manual/en/arrayaccess.offsetunset.php + * + * @param string $offset The offset to unset. + * + * @return void + * + * @access public + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) + { + $offsetlower = strtolower($offset); + unset($this->data[$offsetlower]); + unset($this->keys[$offsetlower]); + } + + /** + * Offset Get + * + * Return the stored data at the provided offset. The offset is converted to + * lowercase and the lookup is done on the data store directly. + * + * @see https://secure.php.net/manual/en/arrayaccess.offsetget.php + * + * @param string $offset Offset to lookup. + * + * @return mixed The data stored at the offset. + * + * @access public + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + $offsetlower = strtolower($offset); + return isset($this->data[$offsetlower]) ? $this->data[$offsetlower] : null; + } + + /** + * Count + * + * @see https://secure.php.net/manual/en/countable.count.php + * + * @param void + * + * @return integer The number of elements stored in the array. + * + * @access public + */ + #[\ReturnTypeWillChange] + public function count() + { + return (int) count($this->data); + } + + /** + * Current + * + * @see https://secure.php.net/manual/en/iterator.current.php + * + * @param void + * + * @return mixed Data at the current position. + * + * @access public + */ + #[\ReturnTypeWillChange] + public function current() + { + return current($this->data); + } + + /** + * Next + * + * @see https://secure.php.net/manual/en/iterator.next.php + * + * @param void + * + * @return void + * + * @access public + */ + #[\ReturnTypeWillChange] + public function next() + { + next($this->data); + } + + /** + * Key + * + * @see https://secure.php.net/manual/en/iterator.key.php + * + * @param void + * + * @return mixed Case-sensitive key at current position. + * + * @access public + */ + #[\ReturnTypeWillChange] + public function key() + { + $key = key($this->data); + return isset($this->keys[$key]) ? $this->keys[$key] : $key; + } + + /** + * Valid + * + * @see https://secure.php.net/manual/en/iterator.valid.php + * + * @return bool If the current position is valid. + * + * @access public + */ + #[\ReturnTypeWillChange] + public function valid() + { + return (bool) (key($this->data) !== null); + } + + /** + * Rewind + * + * @see https://secure.php.net/manual/en/iterator.rewind.php + * + * @param void + * + * @return void + * + * @access public + */ + #[\ReturnTypeWillChange] + public function rewind() + { + reset($this->data); + } +} diff --git a/web/app/vendor/php-curl-class/php-curl-class/src/Curl/Curl.php b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/Curl.php new file mode 100644 index 0000000..ff58b43 --- /dev/null +++ b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/Curl.php @@ -0,0 +1,2057 @@ + + // CTL = + // separators = "(" | ")" | "<" | ">" | "@" + // | "," | ";" | ":" | "\" | <"> + // | "/" | "[" | "]" | "?" | "=" + // | "{" | "}" | SP | HT + // SP = + // HT = + // <"> = + '!', '#', '$', '%', '&', "'", '*', '+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', + 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '|', '~', + ]; + public static $RFC6265 = [ + // RFC 6265: "US-ASCII characters excluding CTLs, whitespace DQUOTE, comma, semicolon, and backslash". + // %x21 + '!', + // %x23-2B + '#', '$', '%', '&', "'", '(', ')', '*', '+', + // %x2D-3A + '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', + // %x3C-5B + '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', + // %x5D-7E + ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', + ]; + + public $curlErrorCodeConstant; + public $curlErrorCodeConstants; + public $curlOptionCodeConstants; + public $effectiveUrl; + public $rfc2616; + public $rfc6265; + public $totalTime; + + private static $deferredProperties = [ + 'curlErrorCodeConstant', + 'curlErrorCodeConstants', + 'curlOptionCodeConstants', + 'effectiveUrl', + 'rfc2616', + 'rfc6265', + 'totalTime', + ]; + + /** + * Construct + * + * @access public + * @param $base_url + * @throws \ErrorException + */ + public function __construct($base_url = null, $options = []) + { + if (!extension_loaded('curl')) { + throw new \ErrorException('cURL library is not loaded'); + } + + unset($this->curlErrorCodeConstant); + unset($this->curlErrorCodeConstants); + unset($this->curlOptionCodeConstants); + unset($this->effectiveUrl); + unset($this->rfc2616); + unset($this->rfc6265); + unset($this->totalTime); + + $this->curl = curl_init(); + $this->initialize($base_url, $options); + } + + /** + * Build Post Data + * + * @access public + * @param $data + * + * @return array|string + * @throws \ErrorException + */ + public function buildPostData($data) + { + $binary_data = false; + + // Return JSON-encoded string when the request's content-type is JSON and the data is serializable. + if (isset($this->headers['Content-Type']) && + preg_match($this->jsonPattern, $this->headers['Content-Type']) && + ( + is_array($data) || + ( + is_object($data) && + interface_exists('JsonSerializable', false) && + $data instanceof \JsonSerializable + ) + )) { + $data = \Curl\Encoder::encodeJson($data); + } elseif (is_array($data)) { + // Manually build a single-dimensional array from a multi-dimensional array as using curl_setopt($ch, + // CURLOPT_POSTFIELDS, $data) doesn't correctly handle multi-dimensional arrays when files are + // referenced. + if (ArrayUtil::isArrayMultidim($data)) { + $data = ArrayUtil::arrayFlattenMultidim($data); + } + + // Modify array values to ensure any referenced files are properly handled depending on the support of + // the @filename API or CURLFile usage. This also fixes the warning "curl_setopt(): The usage of the + // @filename API for file uploading is deprecated. Please use the CURLFile class instead". Ignore + // non-file values prefixed with the @ character. + foreach ($data as $key => $value) { + if (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) { + $binary_data = true; + if (class_exists('CURLFile')) { + $data[$key] = new \CURLFile(substr($value, 1)); + } + } elseif ($value instanceof \CURLFile) { + $binary_data = true; + } elseif ($value instanceof \CURLStringFile) { + $binary_data = true; + } + } + } + + if (!$binary_data && + (is_array($data) || is_object($data)) && + ( + !isset($this->headers['Content-Type']) || + !preg_match('/^multipart\/form-data/', $this->headers['Content-Type']) + )) { + // Avoid using http_build_query() as keys with null values are + // unexpectedly excluded from the resulting string. + // + // $ php -a + // php > echo http_build_query(['a' => '1', 'b' => null, 'c' => '3']); + // a=1&c=3 + // php > echo http_build_query(['a' => '1', 'b' => '', 'c' => '3']); + // a=1&b=&c=3 + // + // $data = http_build_query($data, '', '&'); + $data = implode('&', array_map(function ($k, $v) { + // Encode keys and values using urlencode() to match the default + // behavior http_build_query() where $encoding_type is + // PHP_QUERY_RFC1738. + // + // Use strval() as urlencode() expects a string parameter: + // TypeError: urlencode() expects parameter 1 to be string, integer given + // TypeError: urlencode() expects parameter 1 to be string, null given + // + // php_raw_url_encode() + // php_url_encode() + // https://github.com/php/php-src/blob/master/ext/standard/http.c + return urlencode(strval($k)) . '=' . urlencode(strval($v)); + }, array_keys((array)$data), array_values((array)$data))); + } + + return $data; + } + + /** + * Call + * + * @access public + */ + public function call() + { + $args = func_get_args(); + $function = array_shift($args); + if (is_callable($function)) { + array_unshift($args, $this); + call_user_func_array($function, $args); + } + } + + /** + * Close + * + * @access public + */ + public function close() + { + if (is_resource($this->curl) || $this->curl instanceof \CurlHandle) { + curl_close($this->curl); + } + $this->curl = null; + $this->options = null; + $this->jsonDecoder = null; + $this->jsonDecoderArgs = null; + $this->xmlDecoder = null; + $this->xmlDecoderArgs = null; + $this->headerCallbackData = null; + $this->defaultDecoder = null; + } + + /** + * Progress + * + * @access public + * @param $callback callable|null + */ + public function progress($callback) + { + $this->setOpt(CURLOPT_PROGRESSFUNCTION, $callback); + $this->setOpt(CURLOPT_NOPROGRESS, false); + } + + /** + * Delete + * + * @access public + * @param $url + * @param $query_parameters + * @param $data + * + * @return mixed Returns the value provided by exec. + */ + public function delete($url, $query_parameters = [], $data = []) + { + if (is_array($url)) { + $data = $query_parameters; + $query_parameters = $url; + $url = (string)$this->url; + } + + $this->setUrl($url, $query_parameters); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE'); + + // Avoid including a content-length header in DELETE requests unless there is a message body. The following + // would include "Content-Length: 0" in the request header: + // curl_setopt($ch, CURLOPT_POSTFIELDS, []); + // RFC 2616 4.3 Message Body: + // The presence of a message-body in a request is signaled by the + // inclusion of a Content-Length or Transfer-Encoding header field in + // the request's message-headers. + if (!empty($data)) { + $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); + } + return $this->exec(); + } + + /** + * Download + * + * @access public + * @param $url + * @param $mixed_filename + * + * @return boolean + */ + public function download($url, $mixed_filename) + { + // Use tmpfile() or php://temp to avoid "Too many open files" error. + if (is_callable($mixed_filename)) { + $this->downloadCompleteCallback = $mixed_filename; + $this->downloadFileName = null; + $this->fileHandle = tmpfile(); + } else { + $filename = $mixed_filename; + + // Use a temporary file when downloading. Not using a temporary file can cause an error when an existing + // file has already fully completed downloading and a new download is started with the same destination save + // path. The download request will include header "Range: bytes=$filesize-" which is syntactically valid, + // but unsatisfiable. + $download_filename = $filename . '.pccdownload'; + $this->downloadFileName = $download_filename; + + // Attempt to resume download only when a temporary download file exists and is not empty. + if (is_file($download_filename) && $filesize = filesize($download_filename)) { + $first_byte_position = $filesize; + $range = $first_byte_position . '-'; + $this->setRange($range); + $this->fileHandle = fopen($download_filename, 'ab'); + } else { + $this->fileHandle = fopen($download_filename, 'wb'); + } + + // Move the downloaded temporary file to the destination save path. + $this->downloadCompleteCallback = function ($instance, $fh) use ($download_filename, $filename) { + // Close the open file handle before renaming the file. + if (is_resource($fh)) { + fclose($fh); + } + + rename($download_filename, $filename); + }; + } + + $this->setFile($this->fileHandle); + $this->get($url); + + return ! $this->error; + } + + /** + * Fast download + * + * @access private + * @param $url + * @param $filename + * @param $connections + * + * @return boolean + */ + public function _fastDownload($url, $filename, $connections = 4) { + // First we need to retrive the 'Content-Length' header. + // Use GET because not all hosts support HEAD requests. + $this->setOpts([ + CURLOPT_CUSTOMREQUEST => 'GET', + CURLOPT_NOBODY => true, + CURLOPT_HEADER => true, + CURLOPT_ENCODING => '', + ]); + $this->setUrl($url); + $this->exec(); + + $content_length = isset($this->responseHeaders['Content-Length']) ? + $this->responseHeaders['Content-Length'] : null; + + // If content length header is missing, use the normal download. + if (!$content_length) { + return $this->download($url, $filename); + } + + // Try to divide chunk_size equally. + $chunkSize = ceil($content_length / $connections); + + // First bytes. + $offset = 0; + $nextChunk = $chunkSize; + + // We need this later. + $file_parts = []; + + $multi_curl = new MultiCurl(); + $multi_curl->setConcurrency($connections); + + $multi_curl->error(function ($instance) { + return false; + }); + + for ($i = 1; $i <= $connections; $i++) { + // If last chunk then no need to supply it. + // Range starts with 0, so subtract 1. + $nextChunk = $i === $connections ? '' : $nextChunk - 1; + + // Create part file. + $fpath = "$filename.part$i"; + if (is_file($fpath)) { + unlink($fpath); + } + $fp = fopen($fpath, 'w'); + + // Track all fileparts names; we need this later. + $file_parts[] = $fpath; + + $curl = new Curl(); + $curl->setOpt(CURLOPT_ENCODING, ''); + $curl->setRange("$offset-$nextChunk"); + $curl->setFile($fp); + $curl->disableTimeout(); // otherwise download may fail. + $curl->setUrl($url); + + $curl->complete(function () use ($fp) { + fclose($fp); + }); + + $multi_curl->addCurl($curl); + + if ($i !== $connections) { + $offset = $nextChunk + 1; // Add 1 to match offset. + $nextChunk = $nextChunk + $chunkSize; + } + } + + // let the magic begin. + $multi_curl->start(); + + // Concatenate chunks to single. + if (is_file($filename)) { + unlink($filename); + } + $mainfp = fopen($filename, 'w'); + foreach ($file_parts as $part) { + $fp = fopen($part, 'r'); + stream_copy_to_stream($fp, $mainfp); + fclose($fp); + unlink($part); + } + fclose($mainfp); + + return true; + } + + /** + * Exec + * + * @access public + * @param $ch + * + * @return mixed Returns the value provided by parseResponse. + */ + public function exec($ch = null) + { + $this->attempts += 1; + + if ($this->jsonDecoder === null) { + $this->setDefaultJsonDecoder(); + } + if ($this->xmlDecoder === null) { + $this->setDefaultXmlDecoder(); + } + + if ($ch === null) { + $this->responseCookies = []; + $this->call($this->beforeSendCallback); + $this->rawResponse = curl_exec($this->curl); + $this->curlErrorCode = curl_errno($this->curl); + $this->curlErrorMessage = curl_error($this->curl); + } else { + $this->rawResponse = curl_multi_getcontent($ch); + $this->curlErrorMessage = curl_error($ch); + } + $this->curlError = $this->curlErrorCode !== 0; + + // Ensure Curl::rawResponse is a string as curl_exec() can return false. + // Without this, calling strlen($curl->rawResponse) will error when the + // strict types setting is enabled. + if (!is_string($this->rawResponse)) { + $this->rawResponse = ''; + } + + // Transfer the header callback data and release the temporary store to avoid memory leak. + $this->rawResponseHeaders = $this->headerCallbackData->rawResponseHeaders; + $this->responseCookies = $this->headerCallbackData->responseCookies; + $this->headerCallbackData->rawResponseHeaders = ''; + $this->headerCallbackData->responseCookies = []; + $this->headerCallbackData->stopRequestDecider = null; + $this->headerCallbackData->stopRequest = false; + + // Include additional error code information in error message when possible. + if ($this->curlError) { + $curl_error_message = curl_strerror($this->curlErrorCode); + + if ($this->curlErrorCodeConstant !== '') { + $curl_error_message .= ' (' . $this->curlErrorCodeConstant . ')'; + } + + if (!empty($this->curlErrorMessage)) { + $curl_error_message .= ': ' . $this->curlErrorMessage; + } + + $this->curlErrorMessage = $curl_error_message; + } + + $this->httpStatusCode = $this->getInfo(CURLINFO_HTTP_CODE); + $this->httpError = in_array((int) floor($this->httpStatusCode / 100), [4, 5], true); + $this->error = $this->curlError || $this->httpError; + $this->errorCode = $this->error ? ($this->curlError ? $this->curlErrorCode : $this->httpStatusCode) : 0; + + // NOTE: CURLINFO_HEADER_OUT set to true is required for requestHeaders + // to not be empty (e.g. $curl->setOpt(CURLINFO_HEADER_OUT, true);). + if ($this->getOpt(CURLINFO_HEADER_OUT) === true) { + $this->requestHeaders = $this->parseRequestHeaders($this->getInfo(CURLINFO_HEADER_OUT)); + } + $this->responseHeaders = $this->parseResponseHeaders($this->rawResponseHeaders); + $this->response = $this->parseResponse($this->responseHeaders, $this->rawResponse); + + $this->httpErrorMessage = ''; + if ($this->error) { + if (isset($this->responseHeaders['Status-Line'])) { + $this->httpErrorMessage = $this->responseHeaders['Status-Line']; + } + } + $this->errorMessage = $this->curlError ? $this->curlErrorMessage : $this->httpErrorMessage; + + // Reset select deferred properties so that they may be recalculated. + unset($this->curlErrorCodeConstant); + unset($this->effectiveUrl); + unset($this->totalTime); + + // Reset content-length header possibly set from a PUT or SEARCH request. + $this->unsetHeader('Content-Length'); + + // Reset nobody setting possibly set from a HEAD request. + $this->setOpt(CURLOPT_NOBODY, false); + + // Allow multicurl to attempt retry as needed. + if ($this->isChildOfMultiCurl()) { + return; + } + + if ($this->attemptRetry()) { + return $this->exec($ch); + } + + $this->execDone(); + + return $this->response; + } + + public function execDone() + { + if ($this->error) { + $this->call($this->errorCallback); + } else { + $this->call($this->successCallback); + } + + $this->call($this->completeCallback); + + // Close open file handles and reset the curl instance. + if ($this->fileHandle !== null) { + $this->downloadComplete($this->fileHandle); + } + } + + /** + * Get + * + * @access public + * @param $url + * @param $data + * + * @return mixed Returns the value provided by exec. + */ + public function get($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = (string)$this->url; + } + $this->setUrl($url, $data); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); + $this->setOpt(CURLOPT_HTTPGET, true); + return $this->exec(); + } + + /** + * Get Info + * + * @access public + * @param $opt + * + * @return mixed + */ + public function getInfo($opt = null) + { + $args = []; + $args[] = $this->curl; + + if (func_num_args()) { + $args[] = $opt; + } + + return call_user_func_array('curl_getinfo', $args); + } + + /** + * Head + * + * @access public + * @param $url + * @param $data + * + * @return mixed Returns the value provided by exec. + */ + public function head($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = (string)$this->url; + } + $this->setUrl($url, $data); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD'); + $this->setOpt(CURLOPT_NOBODY, true); + return $this->exec(); + } + + /** + * Options + * + * @access public + * @param $url + * @param $data + * + * @return mixed Returns the value provided by exec. + */ + public function options($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = (string)$this->url; + } + $this->setUrl($url, $data); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS'); + return $this->exec(); + } + + /** + * Patch + * + * @access public + * @param $url + * @param $data + * + * @return mixed Returns the value provided by exec. + */ + public function patch($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = (string)$this->url; + } + + if (is_array($data) && empty($data)) { + $this->removeHeader('Content-Length'); + } + + $this->setUrl($url); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH'); + $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); + return $this->exec(); + } + + /** + * Post + * + * @access public + * @param $url + * @param $data + * @param $follow_303_with_post + * If true, will cause 303 redirections to be followed using a POST request (default: false). + * Notes: + * - Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true. + * - According to the HTTP specs (see [1]), a 303 redirection should be followed using + * the GET method. 301 and 302 must not. + * - In order to force a 303 redirection to be performed using the same method, the + * underlying cURL object must be set in a special state (the CURLOPT_CUSTOMREQUEST + * option must be set to the method to use after the redirection). Due to a limitation + * of the cURL extension of PHP < 5.5.11 ([2], [3]), it is not possible to reset this + * option. Using these PHP engines, it is therefore impossible to restore this behavior + * on an existing php-curl-class Curl object. + * + * @return mixed Returns the value provided by exec. + * + * [1] https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2 + * [2] https://github.com/php/php-src/pull/531 + * [3] http://php.net/ChangeLog-5.php#5.5.11 + */ + public function post($url, $data = '', $follow_303_with_post = false) + { + if (is_array($url)) { + $follow_303_with_post = (bool)$data; + $data = $url; + $url = (string)$this->url; + } + + $this->setUrl($url); + + // Set the request method to "POST" when following a 303 redirect with + // an additional POST request is desired. This is equivalent to setting + // the -X, --request command line option where curl won't change the + // request method according to the HTTP 30x response code. + if ($follow_303_with_post) { + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST'); + } elseif (isset($this->options[CURLOPT_CUSTOMREQUEST])) { + // Unset the CURLOPT_CUSTOMREQUEST option so that curl does not use + // a POST request after a post/redirect/get redirection. Without + // this, curl will use the method string specified for all requests. + $this->setOpt(CURLOPT_CUSTOMREQUEST, null); + } + + $this->setOpt(CURLOPT_POST, true); + $this->setOpt(CURLOPT_POSTFIELDS, $this->buildPostData($data)); + return $this->exec(); + } + + /** + * Put + * + * @access public + * @param $url + * @param $data + * + * @return mixed Returns the value provided by exec. + */ + public function put($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = (string)$this->url; + } + $this->setUrl($url); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT'); + $put_data = $this->buildPostData($data); + if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { + if (is_string($put_data)) { + $this->setHeader('Content-Length', strlen($put_data)); + } + } + if (!empty($put_data)) { + $this->setOpt(CURLOPT_POSTFIELDS, $put_data); + } + return $this->exec(); + } + + /** + * Search + * + * @access public + * @param $url + * @param $data + * + * @return mixed Returns the value provided by exec. + */ + public function search($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = (string)$this->url; + } + $this->setUrl($url); + $this->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH'); + $put_data = $this->buildPostData($data); + if (empty($this->options[CURLOPT_INFILE]) && empty($this->options[CURLOPT_INFILESIZE])) { + if (is_string($put_data)) { + $this->setHeader('Content-Length', strlen($put_data)); + } + } + if (!empty($put_data)) { + $this->setOpt(CURLOPT_POSTFIELDS, $put_data); + } + return $this->exec(); + } + + /** + * Set Cookie + * + * @access public + * @param $key + * @param $value + */ + public function setCookie($key, $value) + { + $this->setEncodedCookie($key, $value); + $this->buildCookies(); + } + + /** + * Set Cookies + * + * @access public + * @param $cookies + */ + public function setCookies($cookies) + { + foreach ($cookies as $key => $value) { + $this->setEncodedCookie($key, $value); + } + $this->buildCookies(); + } + + /** + * Get Cookie + * + * @access public + * @param $key + * + * @return mixed + */ + public function getCookie($key) + { + return $this->getResponseCookie($key); + } + + /** + * Get Response Cookie + * + * @access public + * @param $key + * + * @return mixed + */ + public function getResponseCookie($key) + { + return isset($this->responseCookies[$key]) ? $this->responseCookies[$key] : null; + } + + /** + * Set Max Filesize + * + * @access public + * @param $bytes + */ + public function setMaxFilesize($bytes) + { + $callback = function ($resource, $download_size, $downloaded, $upload_size, $uploaded) use ($bytes) { + // Abort the transfer when $downloaded bytes exceeds maximum $bytes by returning a non-zero value. + return $downloaded > $bytes ? 1 : 0; + }; + $this->progress($callback); + } + + /** + * Set Cookie String + * + * @access public + * @param $string + * + * @return bool + */ + public function setCookieString($string) + { + return $this->setOpt(CURLOPT_COOKIE, $string); + } + + /** + * Set Cookie File + * + * @access public + * @param $cookie_file + * + * @return boolean + */ + public function setCookieFile($cookie_file) + { + return $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file); + } + + /** + * Set Cookie Jar + * + * @access public + * @param $cookie_jar + * + * @return boolean + */ + public function setCookieJar($cookie_jar) + { + return $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar); + } + + /** + * Set Default JSON Decoder + * + * @access public + * @param $assoc + * @param $depth + * @param $options + */ + public function setDefaultJsonDecoder() + { + $this->jsonDecoder = '\Curl\Decoder::decodeJson'; + $this->jsonDecoderArgs = func_get_args(); + } + + /** + * Set Default XML Decoder + * + * @access public + * @param $class_name + * @param $options + * @param $ns + * @param $is_prefix + */ + public function setDefaultXmlDecoder() + { + $this->xmlDecoder = '\Curl\Decoder::decodeXml'; + $this->xmlDecoderArgs = func_get_args(); + } + + /** + * Set Default Decoder + * + * @access public + * @param $mixed boolean|callable|string + */ + public function setDefaultDecoder($mixed = 'json') + { + if ($mixed === false) { + $this->defaultDecoder = false; + } elseif ($mixed === 'json') { + $this->defaultDecoder = '\Curl\Decoder::decodeJson'; + } elseif ($mixed === 'xml') { + $this->defaultDecoder = '\Curl\Decoder::decodeXml'; + } elseif (is_callable($mixed)) { + $this->defaultDecoder = $mixed; + } + } + + /** + * Set Default Header Out + * + * @access public + */ + public function setDefaultHeaderOut() + { + $this->setOpt(CURLINFO_HEADER_OUT, true); + } + + /** + * Set Default Timeout + * + * @access public + */ + public function setDefaultTimeout() + { + $this->setTimeout(self::DEFAULT_TIMEOUT); + } + + /** + * Set Default User Agent + * + * @access public + */ + public function setDefaultUserAgent() + { + $user_agent = 'PHP-Curl-Class/' . self::VERSION . ' (+https://github.com/php-curl-class/php-curl-class)'; + $user_agent .= ' PHP/' . PHP_VERSION; + $curl_version = curl_version(); + $user_agent .= ' curl/' . $curl_version['version']; + $this->setUserAgent($user_agent); + } + + /** + * Set Header + * + * Add extra header to include in the request. + * + * @access public + * @param $key + * @param $value + */ + public function setHeader($key, $value) + { + $this->headers[$key] = $value; + $headers = []; + foreach ($this->headers as $key => $value) { + $headers[] = $key . ': ' . $value; + } + $this->setOpt(CURLOPT_HTTPHEADER, $headers); + } + + /** + * Set Headers + * + * Add extra headers to include in the request. + * + * @access public + * @param $headers + */ + public function setHeaders($headers) + { + if (ArrayUtil::isArrayAssoc($headers)) { + foreach ($headers as $key => $value) { + $key = trim($key); + $value = trim($value); + $this->headers[$key] = $value; + } + } else { + foreach ($headers as $header) { + list($key, $value) = explode(':', $header, 2); + $key = trim($key); + $value = trim($value); + $this->headers[$key] = $value; + } + } + + $headers = []; + foreach ($this->headers as $key => $value) { + $headers[] = $key . ': ' . $value; + } + + $this->setOpt(CURLOPT_HTTPHEADER, $headers); + } + + /** + * Set JSON Decoder + * + * @access public + * @param $mixed boolean|callable + */ + public function setJsonDecoder($mixed) + { + if ($mixed === false || is_callable($mixed)) { + $this->jsonDecoder = $mixed; + $this->jsonDecoderArgs = []; + } + } + + /** + * Set XML Decoder + * + * @access public + * @param $mixed boolean|callable + */ + public function setXmlDecoder($mixed) + { + if ($mixed === false || is_callable($mixed)) { + $this->xmlDecoder = $mixed; + $this->xmlDecoderArgs = []; + } + } + + /** + * Set Opt + * + * @access public + * @param $option + * @param $value + * + * @return boolean + */ + public function setOpt($option, $value) + { + $required_options = [ + CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER', + ]; + + if (in_array($option, array_keys($required_options), true) && $value !== true) { + trigger_error($required_options[$option] . ' is a required option', E_USER_WARNING); + } + + $success = curl_setopt($this->curl, $option, $value); + if ($success) { + $this->options[$option] = $value; + } + return $success; + } + + /** + * Set Opts + * + * @access public + * @param $options + * + * @return boolean + * Returns true if all options were successfully set. If an option could not be successfully set, false is + * immediately returned, ignoring any future options in the options array. Similar to curl_setopt_array(). + */ + public function setOpts($options) + { + foreach ($options as $option => $value) { + if (!$this->setOpt($option, $value)) { + return false; + } + } + return true; + } + + /** + * Set Retry + * + * Number of retries to attempt or decider callable. + * + * When using a number of retries to attempt, the maximum number of attempts + * for the request is $maximum_number_of_retries + 1. + * + * When using a callable decider, the request will be retried until the + * function returns a value which evaluates to false. + * + * @access public + * @param $mixed + */ + public function setRetry($mixed) + { + if (is_callable($mixed)) { + $this->retryDecider = $mixed; + } elseif (is_int($mixed)) { + $maximum_number_of_retries = $mixed; + $this->remainingRetries = $maximum_number_of_retries; + } + } + + /** + * Set Url + * + * @access public + * @param $url + * @param $mixed_data + */ + public function setUrl($url, $mixed_data = '') + { + $built_url = Url::buildUrl($url, $mixed_data); + + if ($this->url === null) { + $this->url = (string)new Url($built_url); + } else { + $this->url = (string)new Url($this->url, $built_url); + } + + $this->setOpt(CURLOPT_URL, $this->url); + } + + /** + * Attempt Retry + * + * @access public + */ + public function attemptRetry() + { + $attempt_retry = false; + if ($this->error) { + if ($this->retryDecider === null) { + $attempt_retry = $this->remainingRetries >= 1; + } else { + $attempt_retry = call_user_func($this->retryDecider, $this); + } + if ($attempt_retry) { + $this->retries += 1; + if ($this->remainingRetries) { + $this->remainingRetries -= 1; + } + } + } + return $attempt_retry; + } + + /** + * Unset Header + * + * Remove extra header previously set using Curl::setHeader(). + * + * @access public + * @param $key + */ + public function unsetHeader($key) + { + unset($this->headers[$key]); + $headers = []; + foreach ($this->headers as $key => $value) { + $headers[] = $key . ': ' . $value; + } + $this->setOpt(CURLOPT_HTTPHEADER, $headers); + } + + /** + * Diagnose + * + * @access public + * @param bool $return + */ + public function diagnose($return = false) + { + if ($return) { + ob_start(); + } + + echo "\n"; + echo '--- Begin PHP Curl Class diagnostic output ---' . "\n"; + echo 'PHP Curl Class version: ' . self::VERSION . "\n"; + echo 'PHP version: ' . PHP_VERSION . "\n"; + + $curl_version = curl_version(); + echo 'Curl version: ' . $curl_version['version'] . "\n"; + + if ($this->attempts === 0) { + echo 'No HTTP requests have been made.' . "\n"; + } else { + $request_types = array( + 'DELETE' => $this->getOpt(CURLOPT_CUSTOMREQUEST) === 'DELETE', + 'GET' => $this->getOpt(CURLOPT_CUSTOMREQUEST) === 'GET' || $this->getOpt(CURLOPT_HTTPGET), + 'HEAD' => $this->getOpt(CURLOPT_CUSTOMREQUEST) === 'HEAD', + 'OPTIONS' => $this->getOpt(CURLOPT_CUSTOMREQUEST) === 'OPTIONS', + 'PATCH' => $this->getOpt(CURLOPT_CUSTOMREQUEST) === 'PATCH', + 'POST' => $this->getOpt(CURLOPT_CUSTOMREQUEST) === 'POST' || $this->getOpt(CURLOPT_POST), + 'PUT' => $this->getOpt(CURLOPT_CUSTOMREQUEST) === 'PUT', + 'SEARCH' => $this->getOpt(CURLOPT_CUSTOMREQUEST) === 'SEARCH', + ); + $request_method = ''; + foreach ($request_types as $http_method_name => $http_method_used) { + if ($http_method_used) { + $request_method = $http_method_name; + break; + } + } + $request_url = $this->getOpt(CURLOPT_URL); + $request_options_count = count($this->options); + $request_headers_count = count($this->requestHeaders); + $request_body_empty = empty($this->getOpt(CURLOPT_POSTFIELDS)); + $response_header_length = isset($this->responseHeaders['Content-Length']) ? + $this->responseHeaders['Content-Length'] : '(not specified in response header)'; + $response_calculated_length = is_string($this->rawResponse) ? + strlen($this->rawResponse) : '(' . var_export($this->rawResponse, true) . ')'; + $response_headers_count = count($this->responseHeaders); + + echo + 'Request contained ' . $request_options_count . ' ' . ( + $request_options_count === 1 ? 'option:' : 'options:' + ) . "\n"; + if ($request_options_count) { + $i = 1; + foreach ($this->options as $option => $value) { + echo ' ' . $i . ' '; + if (isset($this->curlOptionCodeConstants[$option])) { + echo $this->curlOptionCodeConstants[$option] . ':'; + } else { + echo $option . ':'; + } + + if (is_string($value)) { + echo ' ' . $value . "\n"; + } elseif (is_int($value)) { + echo ' ' . $value . "\n"; + } elseif (is_bool($value)) { + echo ' ' . ($value ? 'true' : 'false') . "\n"; + } elseif (is_callable($value)) { + echo ' (callable)' . "\n"; + } else { + echo ' ' . gettype($value) . ':' . "\n"; + var_dump($value); + } + $i += 1; + } + } + + echo + 'Sent an HTTP ' . $request_method . ' request to "' . $request_url . '".' . "\n" . + 'Request contained ' . $request_headers_count . ' ' . ( + $request_headers_count === 1 ? 'header:' : 'headers:' + ) . "\n"; + if ($request_headers_count) { + $i = 1; + foreach ($this->requestHeaders as $key => $value) { + echo ' ' . $i . ' ' . $key . ': ' . $value . "\n"; + $i += 1; + } + } + + echo 'Request contained ' . ($request_body_empty ? 'no body' : 'a body') . '.' . "\n"; + + if ($request_headers_count === 0 && ( + $this->getOpt(CURLOPT_VERBOSE) || + $this->getOpt(CURLINFO_HEADER_OUT) !== true + )) { + echo + 'Warning: Request headers (Curl::requestHeaders) are expected to be empty ' . + '(CURLOPT_VERBOSE was enabled or CURLINFO_HEADER_OUT was disabled).' . "\n"; + } + + if (isset($this->responseHeaders['allow'])) { + $allowed_request_types = array_map(function ($v) { + return trim($v); + }, explode(',', strtoupper($this->responseHeaders['allow']))); + foreach ($request_types as $http_method_name => $http_method_used) { + if ($http_method_used && !in_array($http_method_name, $allowed_request_types, true)) { + echo + 'Warning: A ' . $http_method_name . ' request was made, but only the following request ' . + 'types are allowed: ' . implode(', ', $allowed_request_types) . "\n"; + } + } + } + + echo + 'Response contains ' . $response_headers_count . ' ' . ( + $response_headers_count === 1 ? 'header:' : 'headers:' + ) . "\n"; + if ($this->responseHeaders !== null) { + $i = 1; + foreach ($this->responseHeaders as $key => $value) { + echo ' ' . $i . ' ' . $key . ': ' . $value . "\n"; + $i += 1; + } + } + + if (!isset($this->responseHeaders['Content-Type'])) { + echo 'Response did not set a content type' . "\n"; + } elseif (preg_match($this->jsonPattern, $this->responseHeaders['Content-Type'])) { + echo 'Response appears to be JSON' . "\n"; + } elseif (preg_match($this->xmlPattern, $this->responseHeaders['Content-Type'])) { + echo 'Response appears to be XML' . "\n"; + } + + if ($this->curlError) { + echo + 'A curl error (' . $this->curlErrorCode . ') occurred ' . + 'with message "' . $this->curlErrorMessage . '".' . "\n"; + } + if (!empty($this->httpStatusCode)) { + echo 'Received an HTTP status code of ' . $this->httpStatusCode . '.' . "\n"; + } + if ($this->httpError) { + echo + 'Received an HTTP ' . $this->httpStatusCode . ' error response ' . + 'with message "' . $this->httpErrorMessage . '".' . "\n"; + } + + if ($this->rawResponse === null) { + echo 'Received no response body (response=null).' . "\n"; + } elseif ($this->rawResponse === '') { + echo 'Received an empty response body (response="").' . "\n"; + } else { + echo 'Received a non-empty response body.' . "\n"; + if (isset($this->responseHeaders['Content-Length'])) { + echo 'Response content length (from content-length header): ' . $response_header_length . "\n"; + } else { + echo 'Response content length (calculated): ' . $response_calculated_length . "\n"; + } + + if (preg_match($this->jsonPattern, $this->responseHeaders['Content-Type'])) { + $parsed_response = json_decode($this->rawResponse, true); + if ($parsed_response !== null) { + $messages = []; + array_walk_recursive($parsed_response, function ($value, $key) use (&$messages) { + if (in_array($key, ['code', 'error', 'message'], true)) { + $message = $key . ': ' . $value; + $messages[] = $message; + } + }); + $messages = array_unique($messages); + + $messages_count = count($messages); + if ($messages_count) { + echo + 'Found ' . $messages_count . ' ' . ($messages_count === 1 ? 'message' : 'messages') . + ' in response:' . "\n"; + + $i = 1; + foreach ($messages as $message) { + echo ' ' . $i . ' ' . $message . "\n"; + $i += 1; + } + } + } + } + } + } + + echo '--- End PHP Curl Class diagnostic output ---' . "\n"; + echo "\n"; + + if ($return) { + $output = ob_get_contents(); + ob_end_clean(); + return $output; + } + } + + /** + * Reset + * + * @access public + */ + public function reset() + { + if (is_resource($this->curl) || $this->curl instanceof \CurlHandle) { + curl_reset($this->curl); + } else { + $this->curl = curl_init(); + } + + $this->setDefaultUserAgent(); + $this->setDefaultTimeout(); + $this->setDefaultHeaderOut(); + + $this->initialize(); + } + + public function getCurl() + { + return $this->curl; + } + + public function getId() + { + return $this->id; + } + + public function isError() + { + return $this->error; + } + + public function getErrorCode() + { + return $this->errorCode; + } + + public function getErrorMessage() + { + return $this->errorMessage; + } + + public function isCurlError() + { + return $this->curlError; + } + + public function getCurlErrorCode() + { + return $this->curlErrorCode; + } + + public function getCurlErrorMessage() + { + return $this->curlErrorMessage; + } + + public function isHttpError() + { + return $this->httpError; + } + + public function getHttpStatusCode() + { + return $this->httpStatusCode; + } + + public function getHttpErrorMessage() + { + return $this->httpErrorMessage; + } + + public function getUrl() + { + return $this->url; + } + + public function getRequestHeaders() + { + return $this->requestHeaders; + } + + public function getResponseHeaders() + { + return $this->responseHeaders; + } + + public function getRawResponseHeaders() + { + return $this->rawResponseHeaders; + } + + public function getResponseCookies() + { + return $this->responseCookies; + } + + public function getResponse() + { + return $this->response; + } + + public function getRawResponse() + { + return $this->rawResponse; + } + + public function getBeforeSendCallback() + { + return $this->beforeSendCallback; + } + + public function getDownloadCompleteCallback() + { + return $this->downloadCompleteCallback; + } + + public function getDownloadFileName() + { + return $this->downloadFileName; + } + + public function getSuccessCallback() + { + return $this->successCallback; + } + + public function getErrorCallback() + { + return $this->errorCallback; + } + + public function getCompleteCallback() + { + return $this->completeCallback; + } + + public function getFileHandle() + { + return $this->fileHandle; + } + + public function getAttempts() + { + return $this->attempts; + } + + public function getRetries() + { + return $this->retries; + } + + public function isChildOfMultiCurl() + { + return $this->childOfMultiCurl; + } + + public function getRemainingRetries() + { + return $this->remainingRetries; + } + + public function getRetryDecider() + { + return $this->retryDecider; + } + + public function getJsonDecoder() + { + return $this->jsonDecoder; + } + + public function getXmlDecoder() + { + return $this->xmlDecoder; + } + + /** + * Destruct + * + * @access public + */ + public function __destruct() + { + $this->close(); + } + + public function __get($name) + { + $return = null; + if (in_array($name, self::$deferredProperties, true) && is_callable([$this, $getter = '_get_' . $name])) { + $return = $this->$name = $this->$getter(); + } + return $return; + } + + /** + * Get Curl Error Code Constants + * + * @access private + */ + private function _get_curlErrorCodeConstants() + { + $constants = get_defined_constants(true); + $filtered_array = array_filter( + $constants['curl'], + function ($key) { + return strpos($key, 'CURLE_') !== false; + }, + ARRAY_FILTER_USE_KEY + ); + $curl_const_by_code = array_flip($filtered_array); + return $curl_const_by_code; + } + + /** + * Get Curl Error Code Constant + * + * @access private + */ + private function _get_curlErrorCodeConstant() + { + $curl_const_by_code = $this->curlErrorCodeConstants; + if (isset($curl_const_by_code[$this->curlErrorCode])) { + return $curl_const_by_code[$this->curlErrorCode]; + } + return ''; + } + + /** + * Get Curl Option Code Constants + * + * @access private + */ + private function _get_curlOptionCodeConstants() + { + $constants = get_defined_constants(true); + $filtered_array = array_filter( + $constants['curl'], + function ($key) { + return strpos($key, 'CURLOPT_') !== false; + }, + ARRAY_FILTER_USE_KEY + ); + $curl_const_by_code = array_flip($filtered_array); + + if (!isset($curl_const_by_code[CURLINFO_HEADER_OUT])) { + $curl_const_by_code[CURLINFO_HEADER_OUT] = 'CURLINFO_HEADER_OUT'; + } + + return $curl_const_by_code; + } + + /** + * Get Effective Url + * + * @access private + */ + private function _get_effectiveUrl() + { + return $this->getInfo(CURLINFO_EFFECTIVE_URL); + } + + /** + * Get RFC 2616 + * + * @access private + */ + private function _get_rfc2616() + { + return array_fill_keys(self::$RFC2616, true); + } + + /** + * Get RFC 6265 + * + * @access private + */ + private function _get_rfc6265() + { + return array_fill_keys(self::$RFC6265, true); + } + + /** + * Get Total Time + * + * @access private + */ + private function _get_totalTime() + { + return $this->getInfo(CURLINFO_TOTAL_TIME); + } + + /** + * Build Cookies + * + * @access private + */ + private function buildCookies() + { + // Avoid changing CURLOPT_COOKIE if there are no cookies set. + if (count($this->cookies)) { + // Avoid using http_build_query() as unnecessary encoding is performed. + // http_build_query($this->cookies, '', '; '); + $this->setOpt(CURLOPT_COOKIE, implode('; ', array_map(function ($k, $v) { + return $k . '=' . $v; + }, array_keys($this->cookies), array_values($this->cookies)))); + } + } + + /** + * Download Complete + * + * @access private + * @param $fh + */ + private function downloadComplete($fh) + { + if ($this->error && is_file((string) $this->downloadFileName)) { + @unlink($this->downloadFileName); + } elseif (!$this->error && $this->downloadCompleteCallback) { + rewind($fh); + $this->call($this->downloadCompleteCallback, $fh); + $this->downloadCompleteCallback = null; + } + + if (is_resource($fh)) { + fclose($fh); + } + + // Fix "PHP Notice: Use of undefined constant STDOUT" when reading the + // PHP script from stdin. Using null causes "Warning: curl_setopt(): + // supplied argument is not a valid File-Handle resource". + if (!defined('STDOUT')) { + define('STDOUT', fopen('php://stdout', 'w')); + } + + // Reset CURLOPT_FILE with STDOUT to avoid: "curl_exec(): CURLOPT_FILE + // resource has gone away, resetting to default". + $this->setFile(STDOUT); + + // Reset CURLOPT_RETURNTRANSFER to tell cURL to return subsequent + // responses as the return value of curl_exec(). Without this, + // curl_exec() will revert to returning boolean values. + $this->setOpt(CURLOPT_RETURNTRANSFER, true); + } + + /** + * Parse Headers + * + * @access private + * @param $raw_headers + * + * @return array + */ + private function parseHeaders($raw_headers) + { + $raw_headers = preg_split('/\r\n/', (string) $raw_headers, -1, PREG_SPLIT_NO_EMPTY); + $http_headers = new CaseInsensitiveArray(); + + $raw_headers_count = count($raw_headers); + for ($i = 1; $i < $raw_headers_count; $i++) { + if (strpos($raw_headers[$i], ':') !== false) { + list($key, $value) = explode(':', $raw_headers[$i], 2); + $key = trim($key); + $value = trim($value); + // Use isset() as array_key_exists() and ArrayAccess are not compatible. + if (isset($http_headers[$key])) { + $http_headers[$key] .= ',' . $value; + } else { + $http_headers[$key] = $value; + } + } + } + + return [isset($raw_headers['0']) ? $raw_headers['0'] : '', $http_headers]; + } + + /** + * Parse Request Headers + * + * @access private + * @param $raw_headers + * + * @return \Curl\CaseInsensitiveArray + */ + private function parseRequestHeaders($raw_headers) + { + $request_headers = new CaseInsensitiveArray(); + list($first_line, $headers) = $this->parseHeaders($raw_headers); + $request_headers['Request-Line'] = $first_line; + foreach ($headers as $key => $value) { + $request_headers[$key] = $value; + } + return $request_headers; + } + + /** + * Parse Response + * + * @access private + * @param $response_headers + * @param $raw_response + * + * @return mixed + * If the response content-type is json: + * Returns the json decoder's return value: A stdClass object when the default json decoder is used. + * If the response content-type is xml: + * Returns the xml decoder's return value: A SimpleXMLElement object when the default xml decoder is used. + * If the response content-type is something else: + * Returns the original raw response unless a default decoder has been set. + * If the response content-type cannot be determined: + * Returns the original raw response. + * If the response content-encoding is gzip: + * Returns the response gzip-decoded. + */ + private function parseResponse($response_headers, $raw_response) + { + $response = $raw_response; + if (isset($response_headers['Content-Type'])) { + if (preg_match($this->jsonPattern, $response_headers['Content-Type'])) { + if ($this->jsonDecoder) { + $args = $this->jsonDecoderArgs; + array_unshift($args, $response); + $response = call_user_func_array($this->jsonDecoder, $args); + } + } elseif (preg_match($this->xmlPattern, $response_headers['Content-Type'])) { + if ($this->xmlDecoder) { + $args = $this->xmlDecoderArgs; + array_unshift($args, $response); + $response = call_user_func_array($this->xmlDecoder, $args); + } + } else { + if ($this->defaultDecoder) { + $response = call_user_func($this->defaultDecoder, $response); + } + } + } + + if (isset($response_headers['Content-Encoding']) && $response_headers['Content-Encoding'] === 'gzip' && + is_string($response)) { + // Use @ to suppress message "Warning gzdecode(): data error". + $decoded_response = @gzdecode($response); + if ($decoded_response !== false) { + $response = $decoded_response; + } + } + + return $response; + } + + /** + * Parse Response Headers + * + * @access private + * @param $raw_response_headers + * + * @return \Curl\CaseInsensitiveArray + */ + private function parseResponseHeaders($raw_response_headers) + { + $response_header_array = explode("\r\n\r\n", $raw_response_headers); + $response_header = ''; + for ($i = count($response_header_array) - 1; $i >= 0; $i--) { + if (isset($response_header_array[$i]) && stripos($response_header_array[$i], 'HTTP/') === 0) { + $response_header = $response_header_array[$i]; + break; + } + } + + $response_headers = new CaseInsensitiveArray(); + list($first_line, $headers) = $this->parseHeaders($response_header); + $response_headers['Status-Line'] = $first_line; + foreach ($headers as $key => $value) { + $response_headers[$key] = $value; + } + return $response_headers; + } + + /** + * Set Encoded Cookie + * + * @access private + * @param $key + * @param $value + */ + private function setEncodedCookie($key, $value) + { + $name_chars = []; + foreach (str_split($key) as $name_char) { + if (isset($this->rfc2616[$name_char])) { + $name_chars[] = $name_char; + } else { + $name_chars[] = rawurlencode($name_char); + } + } + + $value_chars = []; + foreach (str_split($value) as $value_char) { + if (isset($this->rfc6265[$value_char])) { + $value_chars[] = $value_char; + } else { + $value_chars[] = rawurlencode($value_char); + } + } + + $this->cookies[implode('', $name_chars)] = implode('', $value_chars); + } + + /** + * Initialize + * + * @access private + * @param $base_url + */ + private function initialize($base_url = null, $options = []) + { + if (isset($options)) { + $this->setOpts($options); + } + + $this->id = uniqid('', true); + + // Only set default user agent if not already set. + if (!array_key_exists(CURLOPT_USERAGENT, $this->options)) { + $this->setDefaultUserAgent(); + } + + // Only set default timeout if not already set. + if (!array_key_exists(CURLOPT_TIMEOUT, $this->options)) { + $this->setDefaultTimeout(); + } + + if (!array_key_exists(CURLINFO_HEADER_OUT, $this->options)) { + $this->setDefaultHeaderOut(); + } + + // Create a placeholder to temporarily store the header callback data. + $header_callback_data = new \stdClass(); + $header_callback_data->rawResponseHeaders = ''; + $header_callback_data->responseCookies = []; + $header_callback_data->stopRequestDecider = null; + $header_callback_data->stopRequest = false; + $this->headerCallbackData = $header_callback_data; + $this->setStop(); + $this->setOpt(CURLOPT_HEADERFUNCTION, createHeaderCallback($header_callback_data)); + + $this->setOpt(CURLOPT_RETURNTRANSFER, true); + $this->headers = new CaseInsensitiveArray(); + + if ($base_url !== null) { + $this->setUrl($base_url); + } + } + + /** + * Set Stop + * + * Specify a callable decider to stop the request early without waiting for + * the full response to be received. + * + * The callable is passed two parameters. The first is the cURL resource, + * the second is a string with header data. Both parameters match the + * parameters in the CURLOPT_HEADERFUNCTION callback. + * + * The callable must return a truthy value for the request to be stopped + * early. + * + * The callable may be set to null to avoid calling the stop request decider + * callback and instead just check the value of stopRequest for attempting + * to stop the request as used by Curl::stop(). + * + * @access public + * @param $callback callable|null + */ + public function setStop($callback = null) + { + $this->headerCallbackData->stopRequestDecider = $callback; + $this->headerCallbackData->stopRequest = false; + + $header_callback_data = $this->headerCallbackData; + $this->progress(createStopRequestFunction($header_callback_data)); + } + + /** + * Stop + * + * Attempt to stop request. + * + * Used by MultiCurl::stop() when making multiple parallel requests. + * + * @access public + */ + public function stop() + { + $this->headerCallbackData->stopRequest = true; + } +} + +/** + * Create Header Callback + * + * Gather headers and parse cookies as response headers are received. Keep this function separate from the class so that + * unset($curl) automatically calls __destruct() as expected. Otherwise, manually calling $curl->close() will be + * necessary to prevent a memory leak. + * + * @param $header_callback_data + * + * @return callable + */ +function createHeaderCallback($header_callback_data) { + return function ($ch, $header) use ($header_callback_data) { + if (preg_match('/^Set-Cookie:\s*([^=]+)=([^;]+)/mi', $header, $cookie) === 1) { + $header_callback_data->responseCookies[$cookie[1]] = trim($cookie[2], " \n\r\t\0\x0B"); + } + + if ($header_callback_data->stopRequestDecider !== null) { + $stop_request_decider = $header_callback_data->stopRequestDecider; + if ($stop_request_decider($ch, $header)) { + $header_callback_data->stopRequest = true; + } + } + + $header_callback_data->rawResponseHeaders .= $header; + return strlen($header); + }; +} + +/** + * Create Stop Request Function + * + * Create a function for Curl::progress() that stops a request early when the + * stopRequest flag is on. Keep this function separate from the class to prevent + * a memory leak. + * + * @param $header_callback_data + * + * @return callable + */ +function createStopRequestFunction($header_callback_data) { + return function ( + $resource, + $download_size, + $downloaded, + $upload_size, + $uploaded + ) use ( + $header_callback_data + ) { + // Abort the transfer when the stop request flag has been set by returning a non-zero value. + return $header_callback_data->stopRequest ? 1 : 0; + }; +} diff --git a/web/app/vendor/php-curl-class/php-curl-class/src/Curl/Decoder.php b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/Decoder.php new file mode 100644 index 0000000..603f4c3 --- /dev/null +++ b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/Decoder.php @@ -0,0 +1,45 @@ +multiCurl = curl_multi_init(); + $this->headers = new CaseInsensitiveArray(); + + if ($base_url !== null) { + $this->setUrl($base_url); + } + } + + /** + * Add Delete + * + * @access public + * @param $url + * @param $query_parameters + * @param $data + * + * @return object + */ + public function addDelete($url, $query_parameters = [], $data = []) + { + if (is_array($url)) { + $data = $query_parameters; + $query_parameters = $url; + $url = $this->baseUrl; + } + + $curl = new Curl($this->baseUrl, $this->options); + $this->queueHandle($curl); + $this->setUrl($url, $query_parameters); + $curl->setUrl($url, $query_parameters); + $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE'); + $curl->setOpt(CURLOPT_POSTFIELDS, $curl->buildPostData($data)); + return $curl; + } + + /** + * Add Download + * + * @access public + * @param $url + * @param $mixed_filename + * + * @return object + */ + public function addDownload($url, $mixed_filename) + { + $curl = new Curl($this->baseUrl, $this->options); + $this->queueHandle($curl); + $this->setUrl($url); + $curl->setUrl($url); + + // Use tmpfile() or php://temp to avoid "Too many open files" error. + if (is_callable($mixed_filename)) { + $curl->downloadCompleteCallback = $mixed_filename; + $curl->downloadFileName = null; + $curl->fileHandle = tmpfile(); + } else { + $filename = $mixed_filename; + + // Use a temporary file when downloading. Not using a temporary file can cause an error when an existing + // file has already fully completed downloading and a new download is started with the same destination save + // path. The download request will include header "Range: bytes=$filesize-" which is syntactically valid, + // but unsatisfiable. + $download_filename = $filename . '.pccdownload'; + $curl->downloadFileName = $download_filename; + + // Attempt to resume download only when a temporary download file exists and is not empty. + if (is_file($download_filename) && $filesize = filesize($download_filename)) { + $first_byte_position = $filesize; + $range = $first_byte_position . '-'; + $curl->setRange($range); + $curl->fileHandle = fopen($download_filename, 'ab'); + + // Move the downloaded temporary file to the destination save path. + $curl->downloadCompleteCallback = function ($instance, $fh) use ($download_filename, $filename) { + // Close the open file handle before renaming the file. + if (is_resource($fh)) { + fclose($fh); + } + + rename($download_filename, $filename); + }; + } else { + $curl->fileHandle = fopen('php://temp', 'wb'); + $curl->downloadCompleteCallback = function ($instance, $fh) use ($filename) { + file_put_contents($filename, stream_get_contents($fh)); + }; + } + } + + $curl->setFile($curl->fileHandle); + $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); + $curl->setOpt(CURLOPT_HTTPGET, true); + return $curl; + } + + /** + * Add Get + * + * @access public + * @param $url + * @param $data + * + * @return object + */ + public function addGet($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + + $curl = new Curl($this->baseUrl, $this->options); + $this->queueHandle($curl); + $this->setUrl($url, $data); + $curl->setUrl($url, $data); + $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'GET'); + $curl->setOpt(CURLOPT_HTTPGET, true); + return $curl; + } + + /** + * Add Head + * + * @access public + * @param $url + * @param $data + * + * @return object + */ + public function addHead($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + + $curl = new Curl($this->baseUrl, $this->options); + $this->queueHandle($curl); + $this->setUrl($url, $data); + $curl->setUrl($url, $data); + $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'HEAD'); + $curl->setOpt(CURLOPT_NOBODY, true); + return $curl; + } + + /** + * Add Options + * + * @access public + * @param $url + * @param $data + * + * @return object + */ + public function addOptions($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + + $curl = new Curl($this->baseUrl, $this->options); + $this->queueHandle($curl); + $this->setUrl($url, $data); + $curl->setUrl($url, $data); + $curl->removeHeader('Content-Length'); + $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'OPTIONS'); + return $curl; + } + + /** + * Add Patch + * + * @access public + * @param $url + * @param $data + * + * @return object + */ + public function addPatch($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + + $curl = new Curl($this->baseUrl, $this->options); + + if (is_array($data) && empty($data)) { + $curl->removeHeader('Content-Length'); + } + + $this->queueHandle($curl); + $this->setUrl($url); + $curl->setUrl($url); + $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH'); + $curl->setOpt(CURLOPT_POSTFIELDS, $curl->buildPostData($data)); + return $curl; + } + + /** + * Add Post + * + * @access public + * @param $url + * @param $data + * @param $follow_303_with_post + * If true, will cause 303 redirections to be followed using a POST request (default: false). + * Note: Redirections are only followed if the CURLOPT_FOLLOWLOCATION option is set to true. + * + * @return object + */ + public function addPost($url, $data = '', $follow_303_with_post = false) + { + if (is_array($url)) { + $follow_303_with_post = (bool)$data; + $data = $url; + $url = $this->baseUrl; + } + + $curl = new Curl($this->baseUrl, $this->options); + $this->queueHandle($curl); + $this->setUrl($url); + + if (is_array($data) && empty($data)) { + $curl->removeHeader('Content-Length'); + } + + $curl->setUrl($url); + + // Set the request method to "POST" when following a 303 redirect with + // an additional POST request is desired. This is equivalent to setting + // the -X, --request command line option where curl won't change the + // request method according to the HTTP 30x response code. + if ($follow_303_with_post) { + $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'POST'); + } + + $curl->setOpt(CURLOPT_POST, true); + $curl->setOpt(CURLOPT_POSTFIELDS, $curl->buildPostData($data)); + return $curl; + } + + /** + * Add Put + * + * @access public + * @param $url + * @param $data + * + * @return object + */ + public function addPut($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + + $curl = new Curl($this->baseUrl, $this->options); + $this->queueHandle($curl); + $this->setUrl($url); + $curl->setUrl($url); + $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT'); + $put_data = $curl->buildPostData($data); + if (is_string($put_data)) { + $curl->setHeader('Content-Length', strlen($put_data)); + } + $curl->setOpt(CURLOPT_POSTFIELDS, $put_data); + return $curl; + } + + /** + * Add Search + * + * @access public + * @param $url + * @param $data + * + * @return object + */ + public function addSearch($url, $data = []) + { + if (is_array($url)) { + $data = $url; + $url = $this->baseUrl; + } + + $curl = new Curl($this->baseUrl, $this->options); + $this->queueHandle($curl); + $this->setUrl($url); + $curl->setUrl($url); + $curl->setOpt(CURLOPT_CUSTOMREQUEST, 'SEARCH'); + $put_data = $curl->buildPostData($data); + if (is_string($put_data)) { + $curl->setHeader('Content-Length', strlen($put_data)); + } + $curl->setOpt(CURLOPT_POSTFIELDS, $put_data); + return $curl; + } + + /** + * Add Curl + * + * Add a Curl instance to the handle queue. + * + * @access public + * @param $curl + * + * @return object + */ + public function addCurl(Curl $curl) + { + $this->queueHandle($curl); + return $curl; + } + + /** + * Close + * + * @access public + */ + public function close() + { + foreach ($this->queuedCurls as $curl) { + $curl->close(); + } + + if (is_resource($this->multiCurl) || $this->multiCurl instanceof \CurlMultiHandle) { + curl_multi_close($this->multiCurl); + } + $this->multiCurl = null; + } + + /** + * Set Concurrency + * + * @access public + * @param $concurrency + */ + public function setConcurrency($concurrency) + { + $this->concurrency = $concurrency; + } + + /** + * Set Cookie + * + * @access public + * @param $key + * @param $value + */ + public function setCookie($key, $value) + { + $this->cookies[$key] = $value; + } + + /** + * Set Cookies + * + * @access public + * @param $cookies + */ + public function setCookies($cookies) + { + foreach ($cookies as $key => $value) { + $this->cookies[$key] = $value; + } + } + + /** + * Set Cookie String + * + * @access public + * @param $string + */ + public function setCookieString($string) + { + $this->setOpt(CURLOPT_COOKIE, $string); + } + + /** + * Set Cookie File + * + * @access public + * @param $cookie_file + */ + public function setCookieFile($cookie_file) + { + $this->setOpt(CURLOPT_COOKIEFILE, $cookie_file); + } + + /** + * Set Cookie Jar + * + * @access public + * @param $cookie_jar + */ + public function setCookieJar($cookie_jar) + { + $this->setOpt(CURLOPT_COOKIEJAR, $cookie_jar); + } + + /** + * Set Header + * + * Add extra header to include in the request. + * + * @access public + * @param $key + * @param $value + */ + public function setHeader($key, $value) + { + $this->headers[$key] = $value; + $this->updateHeaders(); + } + + /** + * Set Headers + * + * Add extra headers to include in the request. + * + * @access public + * @param $headers + */ + public function setHeaders($headers) + { + if (ArrayUtil::isArrayAssoc($headers)) { + foreach ($headers as $key => $value) { + $key = trim($key); + $value = trim($value); + $this->headers[$key] = $value; + } + } else { + foreach ($headers as $header) { + list($key, $value) = explode(':', $header, 2); + $key = trim($key); + $value = trim($value); + $this->headers[$key] = $value; + } + } + + $this->updateHeaders(); + } + + /** + * Set JSON Decoder + * + * @access public + * @param $mixed boolean|callable + */ + public function setJsonDecoder($mixed) + { + if ($mixed === false) { + $this->jsonDecoder = false; + } elseif (is_callable($mixed)) { + $this->jsonDecoder = $mixed; + } + } + + /** + * Set XML Decoder + * + * @access public + * @param $mixed boolean|callable + */ + public function setXmlDecoder($mixed) + { + if ($mixed === false) { + $this->xmlDecoder = false; + } elseif (is_callable($mixed)) { + $this->xmlDecoder = $mixed; + } + } + + /** + * Set Proxies + * + * Set proxies to tunnel requests through. When set, a random proxy will be + * used for the request. + * + * @access public + * @param $proxies array - A list of HTTP proxies to tunnel requests + * through. May include port number. + */ + public function setProxies($proxies) + { + $this->proxies = $proxies; + } + + /** + * Set Opt + * + * @access public + * @param $option + * @param $value + */ + public function setOpt($option, $value) + { + $this->options[$option] = $value; + + // Make changing the url an instance-specific option. Set the value of + // existing instances when they have not already been set to avoid + // unexpectedly changing the request url after is has been specified. + if ($option === CURLOPT_URL) { + foreach ($this->queuedCurls as $curl_id => $curl) { + if (!isset($this->instanceSpecificOptions[$curl_id][$option]) || + $this->instanceSpecificOptions[$curl_id][$option] === null) { + $this->instanceSpecificOptions[$curl_id][$option] = $value; + } + } + } + } + + /** + * Set Opts + * + * @access public + * @param $options + */ + public function setOpts($options) + { + foreach ($options as $option => $value) { + $this->setOpt($option, $value); + } + } + + /** + * Set Rate Limit + * + * @access public + * @param $rate_limit string (e.g. "60/1m"). + * @throws \UnexpectedValueException + */ + public function setRateLimit($rate_limit) + { + $rate_limit_pattern = + '/' . // delimiter + '^' . // assert start + '(\d+)' . // digit(s) + '\/' . // slash + '(\d+)?' . // digit(s), optional + '(s|m|h)' . // unit, s for seconds, m for minutes, h for hours + '$' . // assert end + '/' . // delimiter + 'i' . // case-insensitive matches + ''; + if (!preg_match($rate_limit_pattern, $rate_limit, $matches)) { + throw new \UnexpectedValueException( + 'rate limit must be formatted as $max_requests/$interval(s|m|h) ' . + '(e.g. "60/1m" for a maximum of 60 requests per 1 minute)' + ); + } + + $max_requests = (int)$matches['1']; + if ($matches['2'] === '') { + $interval = 1; + } else { + $interval = (int)$matches['2']; + } + $unit = strtolower($matches['3']); + + // Convert interval to seconds based on unit. + if ($unit === 's') { + $interval_seconds = $interval * 1; + } elseif ($unit === 'm') { + $interval_seconds = $interval * 60; + } elseif ($unit === 'h') { + $interval_seconds = $interval * 3600; + } + + $this->rateLimit = $max_requests . '/' . $interval . $unit; + $this->rateLimitEnabled = true; + $this->maxRequests = $max_requests; + $this->interval = $interval; + $this->intervalSeconds = $interval_seconds; + $this->unit = $unit; + } + + /** + * Set Retry + * + * Number of retries to attempt or decider callable. + * + * When using a number of retries to attempt, the maximum number of attempts + * for the request is $maximum_number_of_retries + 1. + * + * When using a callable decider, the request will be retried until the + * function returns a value which evaluates to false. + * + * @access public + * @param $mixed + */ + public function setRetry($mixed) + { + $this->retry = $mixed; + } + + /** + * Set Url + * + * @access public + * @param $url + * @param $mixed_data + */ + public function setUrl($url, $mixed_data = '') + { + $built_url = Url::buildUrl($url, $mixed_data); + + if ($this->baseUrl === null) { + $this->baseUrl = (string)new Url($built_url); + } else { + $this->baseUrl = (string)new Url($this->baseUrl, $built_url); + } + + $this->setOpt(CURLOPT_URL, $this->baseUrl); + } + + /** + * Start + * + * @access public + * @throws \ErrorException + */ + public function start() + { + if ($this->isStarted) { + return; + } + + $this->isStarted = true; + $this->startTime = microtime(true); + $this->currentStartTime = microtime(true); + $this->currentRequestCount = 0; + + do { + while (count($this->queuedCurls) && + count($this->activeCurls) < $this->concurrency && + (!$this->rateLimitEnabled || $this->hasRequestQuota()) + ) { + $this->initHandle(); + } + + if ($this->rateLimitEnabled && !count($this->activeCurls) && !$this->hasRequestQuota()) { + $this->waitUntilRequestQuotaAvailable(); + } + + if ($this->preferRequestTimeAccuracy) { + // Wait for activity on any curl_multi connection when curl_multi_select (libcurl) fails to correctly + // block. + // https://bugs.php.net/bug.php?id=63411 + // + // Also, use a shorter curl_multi_select() timeout instead the default of one second. This allows + // pending requests to have more accurate start times. Without a shorter timeout, it can be nearly a + // full second before available request quota is rechecked and pending requests can be initialized. + if (curl_multi_select($this->multiCurl, 0.2) === -1) { + usleep(100000); + } + + curl_multi_exec($this->multiCurl, $active); + } else { + // Use multiple loops to get data off of the multi handler. Without this, the following error may appear + // intermittently on certain versions of PHP: + // curl_multi_exec(): supplied resource is not a valid cURL handle resource + + // Clear out the curl buffer. + do { + $status = curl_multi_exec($this->multiCurl, $active); + } while ($status === CURLM_CALL_MULTI_PERFORM); + + // Wait for more information and then get that information. + while ($active && $status === CURLM_OK) { + // Check if the network socket has some data. + if (curl_multi_select($this->multiCurl) !== -1) { + // Process the data for as long as the system tells us to keep getting it. + do { + $status = curl_multi_exec($this->multiCurl, $active); + } while ($status === CURLM_CALL_MULTI_PERFORM); + } + } + } + + while ((is_resource($this->multiCurl) || $this->multiCurl instanceof \CurlMultiHandle) && + (($info_array = curl_multi_info_read($this->multiCurl)) !== false)) { + if ($info_array['msg'] === CURLMSG_DONE) { + foreach ($this->activeCurls as $key => $curl) { + if ($curl->curl === $info_array['handle']) { + // Set the error code for multi handles using the "result" key in the array returned by + // curl_multi_info_read(). Using curl_errno() on a multi handle will incorrectly return 0 + // for errors. + $curl->curlErrorCode = $info_array['result']; + $curl->exec($curl->curl); + + if ($curl->attemptRetry()) { + // Remove completed handle before adding again in order to retry request. + curl_multi_remove_handle($this->multiCurl, $curl->curl); + + $curlm_error_code = curl_multi_add_handle($this->multiCurl, $curl->curl); + if ($curlm_error_code !== CURLM_OK) { + throw new \ErrorException( + 'cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code) + ); + } + + $curl->call($curl->beforeSendCallback); + } else { + $curl->execDone(); + + // Remove completed instance from active curls. + unset($this->activeCurls[$key]); + + // Remove handle of the completed instance. + curl_multi_remove_handle($this->multiCurl, $curl->curl); + + // Clean up completed instance. + $curl->close(); + } + + break; + } + } + } + } + } while ($active || count($this->activeCurls) || count($this->queuedCurls)); + + $this->isStarted = false; + $this->stopTime = microtime(true); + } + + /** + * Stop + * + * @access public + */ + public function stop() + { + // Remove any queued curl requests. + while (count($this->queuedCurls)) { + $curl = array_pop($this->queuedCurls); + $curl->close(); + } + + // Attempt to stop active curl requests. + while (count($this->activeCurls)) { + // Remove instance from active curls. + $curl = array_pop($this->activeCurls); + + // Remove active curl handle. + curl_multi_remove_handle($this->multiCurl, $curl->curl); + + $curl->stop(); + } + } + + /** + * Unset Header + * + * Remove extra header previously set using Curl::setHeader(). + * + * @access public + * @param $key + */ + public function unsetHeader($key) + { + unset($this->headers[$key]); + } + + /** + * Set request time accuracy + * + * @access public + */ + public function setRequestTimeAccuracy() + { + $this->preferRequestTimeAccuracy = true; + } + + /** + * Destruct + * + * @access public + */ + public function __destruct() + { + $this->close(); + } + + /** + * Update Headers + * + * @access private + */ + private function updateHeaders() + { + foreach ($this->queuedCurls as $curl) { + $curl->setHeaders($this->headers); + } + } + + /** + * Queue Handle + * + * @access private + * @param $curl + */ + private function queueHandle($curl) + { + // Use sequential ids to allow for ordered post processing. + $curl->id = $this->nextCurlId++; + $curl->childOfMultiCurl = true; + $this->queuedCurls[$curl->id] = $curl; + + $curl->setHeaders($this->headers); + } + + /** + * Init Handle + * + * @access private + * @param $curl + * @throws \ErrorException + */ + private function initHandle() + { + $curl = array_shift($this->queuedCurls); + if ($curl === null) { + return; + } + + // Add instance to list of active curls. + $this->currentRequestCount += 1; + $this->activeCurls[$curl->id] = $curl; + + // Set callbacks if not already individually set. + if ($curl->beforeSendCallback === null) { + $curl->beforeSend($this->beforeSendCallback); + } + if ($curl->successCallback === null) { + $curl->success($this->successCallback); + } + if ($curl->errorCallback === null) { + $curl->error($this->errorCallback); + } + if ($curl->completeCallback === null) { + $curl->complete($this->completeCallback); + } + + // Set decoders if not already individually set. + if ($curl->jsonDecoder === null) { + $curl->setJsonDecoder($this->jsonDecoder); + } + if ($curl->xmlDecoder === null) { + $curl->setXmlDecoder($this->xmlDecoder); + } + + // Set instance-specific options on the Curl instance when present. + if (isset($this->instanceSpecificOptions[$curl->id])) { + $curl->setOpts($this->instanceSpecificOptions[$curl->id]); + } + + $curl->setRetry($this->retry); + $curl->setCookies($this->cookies); + + // Use a random proxy for the curl instance when proxies have been set + // and the curl instance doesn't already have a proxy set. + if (is_array($this->proxies) && $curl->getOpt(CURLOPT_PROXY) === null) { + $random_proxy = ArrayUtil::arrayRandom($this->proxies); + $curl->setProxy($random_proxy); + } + + $curlm_error_code = curl_multi_add_handle($this->multiCurl, $curl->curl); + if ($curlm_error_code !== CURLM_OK) { + throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)); + } + + $curl->call($curl->beforeSendCallback); + } + + /** + * Has Request Quota + * + * Checks if there is any available quota to make additional requests while + * rate limiting is enabled. + * + * @access private + */ + private function hasRequestQuota() + { + // Calculate if there's request quota since ratelimiting is enabled. + if ($this->rateLimitEnabled) { + // Determine if the limit of requests per interval has been reached. + if ($this->currentRequestCount >= $this->maxRequests) { + $micro_time = microtime(true); + $elapsed_seconds = $micro_time - $this->currentStartTime; + if ($elapsed_seconds <= $this->intervalSeconds) { + $this->rateLimitReached = true; + return false; + } elseif ($this->rateLimitReached) { + $this->rateLimitReached = false; + $this->currentStartTime = $micro_time; + $this->currentRequestCount = 0; + } + } + + return true; + } else { + return true; + } + } + + /** + * Wait Until Request Quota Available + * + * Waits until there is available request quota available based on the rate limit. + * + * @access private + */ + private function waitUntilRequestQuotaAvailable() + { + $sleep_until = $this->currentStartTime + $this->intervalSeconds; + $sleep_seconds = $sleep_until - microtime(true); + + // Avoid using time_sleep_until() as it appears to be less precise and not sleep long enough. + usleep((int) $sleep_seconds * 1000000); + + // Ensure that enough time has passed as usleep() may not have waited long enough. + $this->currentStartTime = microtime(true); + if ($this->currentStartTime < $sleep_until) { + do { + usleep(1000000 / 4); + $this->currentStartTime = microtime(true); + } while ($this->currentStartTime < $sleep_until); + } + + $this->currentRequestCount = 0; + } +} diff --git a/web/app/vendor/php-curl-class/php-curl-class/src/Curl/StringUtil.php b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/StringUtil.php new file mode 100644 index 0000000..86341f9 --- /dev/null +++ b/web/app/vendor/php-curl-class/php-curl-class/src/Curl/StringUtil.php @@ -0,0 +1,65 @@ +baseUrl = $base_url; + $this->relativeUrl = $relative_url; + } + + public function __toString() : string + { + return $this->absolutizeUrl(); + } + + /** + * Remove dot segments. + * + * Interpret and remove the special "." and ".." path segments from a referenced path. + */ + public static function removeDotSegments($input) + { + // 1. The input buffer is initialized with the now-appended path + // components and the output buffer is initialized to the empty + // string. + $output = ''; + + // 2. While the input buffer is not empty, loop as follows: + while (!empty($input)) { + // A. If the input buffer begins with a prefix of "../" or "./", + // then remove that prefix from the input buffer; otherwise, + if (StringUtil::startsWith($input, '../')) { + $input = substr($input, 3); + } elseif (StringUtil::startsWith($input, './')) { + $input = substr($input, 2); + + // B. if the input buffer begins with a prefix of "/./" or "/.", + // where "." is a complete path segment, then replace that + // prefix with "/" in the input buffer; otherwise, + } elseif (StringUtil::startsWith($input, '/./')) { + $input = substr($input, 2); + } elseif ($input === '/.') { + $input = '/'; + + // C. if the input buffer begins with a prefix of "/../" or "/..", + // where ".." is a complete path segment, then replace that + // prefix with "/" in the input buffer and remove the last + // segment and its preceding "/" (if any) from the output + // buffer; otherwise, + } elseif (StringUtil::startsWith($input, '/../')) { + $input = substr($input, 3); + $output = substr_replace($output, '', StringUtil::reversePosition($output, '/')); + } elseif ($input === '/..') { + $input = '/'; + $output = substr_replace($output, '', StringUtil::reversePosition($output, '/')); + + // D. if the input buffer consists only of "." or "..", then remove + // that from the input buffer; otherwise, + } elseif ($input === '.' || $input === '..') { + $input = ''; + + // E. move the first path segment in the input buffer to the end of + // the output buffer, including the initial "/" character (if + // any) and any subsequent characters up to, but not including, + // the next "/" character or the end of the input buffer. + } elseif (!(($pos = StringUtil::position($input, '/', 1)) === false)) { + $output .= substr($input, 0, $pos); + $input = substr_replace($input, '', 0, $pos); + } else { + $output .= $input; + $input = ''; + } + } + + // 3. Finally, the output buffer is returned as the result of + // remove_dot_segments. + return $output . $input; + } + + /** + * Build Url + * + * @access public + * @param $url + * @param $mixed_data + * + * @return string + */ + public static function buildUrl($url, $mixed_data = '') + { + $query_string = ''; + if (!empty($mixed_data)) { + $query_mark = strpos($url, '?') > 0 ? '&' : '?'; + if (is_string($mixed_data)) { + $query_string .= $query_mark . $mixed_data; + } elseif (is_array($mixed_data)) { + $query_string .= $query_mark . http_build_query($mixed_data, '', '&'); + } + } + return $url . $query_string; + } + + /** + * Absolutize url. + * + * Combine the base and relative url into an absolute url. + */ + private function absolutizeUrl() + { + $b = self::parseUrl($this->baseUrl); + if (!isset($b['path'])) { + $b['path'] = '/'; + } + if ($this->relativeUrl === null) { + return $this->unparseUrl($b); + } + $r = self::parseUrl($this->relativeUrl); + $r['authorized'] = isset($r['scheme']) || isset($r['host']) || isset($r['port']) + || isset($r['user']) || isset($r['pass']); + $target = []; + if (isset($r['scheme'])) { + $target['scheme'] = $r['scheme']; + $target['host'] = isset($r['host']) ? $r['host'] : null; + $target['port'] = isset($r['port']) ? $r['port'] : null; + $target['user'] = isset($r['user']) ? $r['user'] : null; + $target['pass'] = isset($r['pass']) ? $r['pass'] : null; + $target['path'] = isset($r['path']) ? self::removeDotSegments($r['path']) : null; + $target['query'] = isset($r['query']) ? $r['query'] : null; + } else { + $target['scheme'] = isset($b['scheme']) ? $b['scheme'] : null; + if ($r['authorized']) { + $target['host'] = isset($r['host']) ? $r['host'] : null; + $target['port'] = isset($r['port']) ? $r['port'] : null; + $target['user'] = isset($r['user']) ? $r['user'] : null; + $target['pass'] = isset($r['pass']) ? $r['pass'] : null; + $target['path'] = isset($r['path']) ? self::removeDotSegments($r['path']) : null; + $target['query'] = isset($r['query']) ? $r['query'] : null; + } else { + $target['host'] = isset($b['host']) ? $b['host'] : null; + $target['port'] = isset($b['port']) ? $b['port'] : null; + $target['user'] = isset($b['user']) ? $b['user'] : null; + $target['pass'] = isset($b['pass']) ? $b['pass'] : null; + if (!isset($r['path']) || $r['path'] === '') { + $target['path'] = $b['path']; + $target['query'] = isset($r['query']) ? $r['query'] : (isset($b['query']) ? $b['query'] : null); + } else { + if (StringUtil::startsWith($r['path'], '/')) { + $target['path'] = self::removeDotSegments($r['path']); + } else { + $base = StringUtil::characterReversePosition($b['path'], '/', true); + if ($base === false) { + $base = ''; + } + $target['path'] = self::removeDotSegments($base . '/' . $r['path']); + } + $target['query'] = isset($r['query']) ? $r['query'] : null; + } + } + } + if ($this->relativeUrl === '') { + $target['fragment'] = isset($b['fragment']) ? $b['fragment'] : null; + } else { + $target['fragment'] = isset($r['fragment']) ? $r['fragment'] : null; + } + $absolutized_url = $this->unparseUrl($target); + return $absolutized_url; + } + + /** + * Parse url. + * + * Parse url into components of a URI as specified by RFC 3986. + */ + public static function parseUrl($url) + { + $parts = parse_url((string) $url); + if (isset($parts['path'])) { + $parts['path'] = self::percentEncodeChars($parts['path']); + } + return $parts; + } + + /** + * Percent-encode characters. + * + * Percent-encode characters to represent a data octet in a component when + * that octet's corresponding character is outside the allowed set. + */ + private static function percentEncodeChars($chars) + { + // ALPHA = A-Z / a-z + $alpha = 'A-Za-z'; + + // DIGIT = 0-9 + $digit = '0-9'; + + // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + $unreserved = $alpha . $digit . preg_quote('-._~'); + + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + // / "*" / "+" / "," / ";" / "=" / "#" + $sub_delims = preg_quote('!$&\'()*+,;=#'); + + // HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" + $hexdig = $digit . 'A-F'; + // "The uppercase hexadecimal digits 'A' through 'F' are equivalent to + // the lowercase digits 'a' through 'f', respectively." + $hexdig .= 'a-f'; + + $pattern = '/(?:[^' . $unreserved . $sub_delims . preg_quote(':@%/?', '/') . ']++|%(?![' . $hexdig . ']{2}))/'; + $percent_encoded_chars = preg_replace_callback( + $pattern, + function ($matches) { + return rawurlencode($matches[0]); + }, + $chars + ); + return $percent_encoded_chars; + } + + /** + * Unparse url. + * + * Combine url components into a url. + */ + private function unparseUrl($parsed_url) + { + $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; + $user = isset($parsed_url['user']) ? $parsed_url['user'] : ''; + $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : ''; + $pass = ($user || $pass) ? $pass . '@' : ''; + $host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; + $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; + $path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; + $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; + $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; + $unparsed_url = $scheme . $user . $pass . $host . $port . $path . $query . $fragment; + return $unparsed_url; + } +} diff --git a/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/PHPCurlClassTest.php b/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/PHPCurlClassTest.php deleted file mode 100644 index c42bea0..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/PHPCurlClassTest.php +++ /dev/null @@ -1,740 +0,0 @@ -assertTrue(extension_loaded('curl')); - } - - public function testArrayAssociative() { - $this->assertTrue(is_array_assoc(array( - 'foo' => 'wibble', - 'bar' => 'wubble', - 'baz' => 'wobble', - ))); - } - - public function testArrayIndexed() { - $this->assertFalse(is_array_assoc(array( - 'wibble', - 'wubble', - 'wobble', - ))); - } - - public function testCaseInsensitiveArrayGet() { - $array = new CaseInsensitiveArray(); - $this->assertTrue(is_object($array)); - $this->assertCount(0, $array); - $this->assertNull($array[(string)rand()]); - - $array['foo'] = 'bar'; - $this->assertNotEmpty($array); - $this->assertCount(1, $array); - } - - public function testCaseInsensitiveArraySet() { - function assertions($array, $count=1) { - PHPUnit_Framework_Assert::assertCount($count, $array); - PHPUnit_Framework_Assert::assertTrue($array['foo'] === 'bar'); - PHPUnit_Framework_Assert::assertTrue($array['Foo'] === 'bar'); - PHPUnit_Framework_Assert::assertTrue($array['FOo'] === 'bar'); - PHPUnit_Framework_Assert::assertTrue($array['FOO'] === 'bar'); - } - - $array = new CaseInsensitiveArray(); - $array['foo'] = 'bar'; - assertions($array); - - $array['Foo'] = 'bar'; - assertions($array); - - $array['FOo'] = 'bar'; - assertions($array); - - $array['FOO'] = 'bar'; - assertions($array); - - $array['baz'] = 'qux'; - assertions($array, 2); - } - - public function testUserAgent() { - $test = new Test(); - $test->curl->setUserAgent(Curl::USER_AGENT); - $this->assertTrue($test->server('server', 'GET', array( - 'key' => 'HTTP_USER_AGENT', - )) === Curl::USER_AGENT); - } - - public function testGet() { - $test = new Test(); - $this->assertTrue($test->server('server', 'GET', array( - 'key' => 'REQUEST_METHOD', - )) === 'GET'); - } - - public function testPostRequestMethod() { - $test = new Test(); - $this->assertTrue($test->server('server', 'POST', array( - 'key' => 'REQUEST_METHOD', - )) === 'POST'); - } - - public function testPostData() { - $test = new Test(); - $this->assertTrue($test->server('post', 'POST', array( - 'key' => 'value', - )) === 'key=value'); - } - - public function testPostAssociativeArrayData() { - $test = new Test(); - $this->assertTrue($test->server('post_multidimensional', 'POST', array( - 'username' => 'myusername', - 'password' => 'mypassword', - 'more_data' => array( - 'param1' => 'something', - 'param2' => 'other thing', - 'param3' => 123, - 'param4' => 3.14, - ), - )) === 'username=myusername&password=mypassword&more_data%5Bparam1%5D=something&more_data%5Bparam2%5D=other%20thing&more_data%5Bparam3%5D=123&more_data%5Bparam4%5D=3.14'); - } - - public function testPostMultidimensionalData() { - $test = new Test(); - $this->assertTrue($test->server('post_multidimensional', 'POST', array( - 'key' => 'file', - 'file' => array( - 'wibble', - 'wubble', - 'wobble', - ), - )) === 'key=file&file%5B%5D=wibble&file%5B%5D=wubble&file%5B%5D=wobble'); - } - - public function testPostFilePathUpload() { - $file_path = get_png(); - - $test = new Test(); - $this->assertTrue($test->server('post_file_path_upload', 'POST', array( - 'key' => 'image', - 'image' => '@' . $file_path, - )) === 'image/png'); - - unlink($file_path); - $this->assertFalse(file_exists($file_path)); - } - - public function testPostCurlFileUpload() { - if (class_exists('CURLFile')) { - $file_path = get_png(); - - $test = new Test(); - $this->assertTrue($test->server('post_file_path_upload', 'POST', array( - 'key' => 'image', - 'image' => new CURLFile($file_path), - )) === 'image/png'); - - unlink($file_path); - $this->assertFalse(file_exists($file_path)); - } - } - - public function testPutRequestMethod() { - $test = new Test(); - $this->assertTrue($test->server('request_method', 'PUT') === 'PUT'); - } - - public function testPutData() { - $test = new Test(); - $this->assertTrue($test->server('put', 'PUT', array( - 'key' => 'value', - )) === 'key=value'); - } - - public function testPutFileHandle() { - $png = create_png(); - $tmp_file = create_tmp_file($png); - - $test = new Test(); - $test->curl->setHeader('X-DEBUG-TEST', 'put_file_handle'); - $test->curl->setOpt(CURLOPT_PUT, true); - $test->curl->setOpt(CURLOPT_INFILE, $tmp_file); - $test->curl->setOpt(CURLOPT_INFILESIZE, strlen($png)); - $test->curl->put(Test::TEST_URL); - - fclose($tmp_file); - - $this->assertTrue($test->curl->response === 'image/png'); - } - - public function testPatchRequestMethod() { - $test = new Test(); - $this->assertTrue($test->server('request_method', 'PATCH') === 'PATCH'); - } - - public function testDelete() { - $test = new Test(); - $this->assertTrue($test->server('server', 'DELETE', array( - 'key' => 'REQUEST_METHOD', - )) === 'DELETE'); - - $test = new Test(); - $this->assertTrue($test->server('delete', 'DELETE', array( - 'test' => 'delete', - 'key' => 'test', - )) === 'delete'); - } - - public function testHeadRequestMethod() { - $test = new Test(); - $test->server('request_method', 'HEAD', array( - 'key' => 'REQUEST_METHOD', - )); - $this->assertEquals($test->curl->response_headers['X-REQUEST-METHOD'], 'HEAD'); - $this->assertEmpty($test->curl->response); - } - - public function testOptionsRequestMethod() { - $test = new Test(); - $test->server('request_method', 'OPTIONS', array( - 'key' => 'REQUEST_METHOD', - )); - $this->assertEquals($test->curl->response_headers['X-REQUEST-METHOD'], 'OPTIONS'); - } - - public function testBasicHttpAuth401Unauthorized() { - $test = new Test(); - $this->assertTrue($test->server('http_basic_auth', 'GET') === 'canceled'); - } - - public function testBasicHttpAuthSuccess() { - $username = 'myusername'; - $password = 'mypassword'; - $test = new Test(); - $test->curl->setBasicAuthentication($username, $password); - $test->server('http_basic_auth', 'GET'); - $json = $test->curl->response; - $this->assertTrue($json->username === $username); - $this->assertTrue($json->password === $password); - } - - public function testReferrer() { - $test = new Test(); - $test->curl->setReferrer('myreferrer'); - $this->assertTrue($test->server('server', 'GET', array( - 'key' => 'HTTP_REFERER', - )) === 'myreferrer'); - } - - public function testCookies() { - $test = new Test(); - $test->curl->setCookie('mycookie', 'yum'); - $this->assertTrue($test->server('cookie', 'GET', array( - 'key' => 'mycookie', - )) === 'yum'); - } - - public function testCookieFile() { - $cookie_file = dirname(__FILE__) . '/cookies.txt'; - $cookie_data = implode("\t", array( - '127.0.0.1', // domain - 'FALSE', // tailmatch - '/', // path - 'FALSE', // secure - '0', // expires - 'mycookie', // name - 'yum', // value - )); - file_put_contents($cookie_file, $cookie_data); - - $test = new Test(); - $test->curl->setCookieFile($cookie_file); - $this->assertTrue($test->server('cookie', 'GET', array( - 'key' => 'mycookie', - )) === 'yum'); - - unlink($cookie_file); - $this->assertFalse(file_exists($cookie_file)); - } - - public function testCookieJar() { - $cookie_file = dirname(__FILE__) . '/cookies.txt'; - - $test = new Test(); - $test->curl->setCookieJar($cookie_file); - $test->server('cookiejar', 'GET'); - $test->curl->close(); - - $this->assertTrue(!(strpos(file_get_contents($cookie_file), "\t" . 'mycookie' . "\t" . 'yum') === false)); - unlink($cookie_file); - $this->assertFalse(file_exists($cookie_file)); - } - - public function testMultipleCookieResponse() { - $expected_response = 'cookie1=scrumptious,cookie2=mouthwatering'; - - // github.com/facebook/hhvm/issues/2345 - if (defined('HHVM_VERSION')) { - $expected_response = 'cookie2=mouthwatering,cookie1=scrumptious'; - } - - $test = new Test(); - $test->server('multiple_cookie', 'GET'); - $this->assertEquals($test->curl->response_headers['Set-Cookie'], $expected_response); - } - - public function testError() { - $test = new Test(); - $test->curl->setOpt(CURLOPT_CONNECTTIMEOUT_MS, 4000); - $test->curl->get(Test::ERROR_URL); - $this->assertTrue($test->curl->error); - $this->assertTrue($test->curl->curl_error); - $this->assertTrue($test->curl->curl_error_code === CURLE_OPERATION_TIMEOUTED); - } - - public function testErrorMessage() { - $test = new Test(); - $test->server('error_message', 'GET'); - - $expected_response = 'HTTP/1.1 401 Unauthorized'; - if (defined('HHVM_VERSION')) { - $expected_response = 'HTTP/1.1 401'; - } - - $this->assertEquals($test->curl->error_message, $expected_response); - } - - public function testHeaders() { - $test = new Test(); - $test->curl->setHeader('Content-Type', 'application/json'); - $test->curl->setHeader('X-Requested-With', 'XMLHttpRequest'); - $test->curl->setHeader('Accept', 'application/json'); - $this->assertTrue($test->server('server', 'GET', array( - 'key' => 'HTTP_CONTENT_TYPE', // OR "CONTENT_TYPE". - )) === 'application/json'); - $this->assertTrue($test->server('server', 'GET', array( - 'key' => 'HTTP_X_REQUESTED_WITH', - )) === 'XMLHttpRequest'); - $this->assertTrue($test->server('server', 'GET', array( - 'key' => 'HTTP_ACCEPT', - )) === 'application/json'); - } - - public function testHeaderCaseSensitivity() { - $content_type = 'application/json'; - $test = new Test(); - $test->curl->setHeader('Content-Type', $content_type); - $test->server('response_header', 'GET'); - - $request_headers = $test->curl->request_headers; - $response_headers = $test->curl->response_headers; - - $this->assertEquals($request_headers['Content-Type'], $content_type); - $this->assertEquals($request_headers['content-type'], $content_type); - $this->assertEquals($request_headers['CONTENT-TYPE'], $content_type); - $this->assertEquals($request_headers['cOnTeNt-TyPe'], $content_type); - - $etag = $response_headers['ETag']; - $this->assertEquals($response_headers['ETAG'], $etag); - $this->assertEquals($response_headers['etag'], $etag); - $this->assertEquals($response_headers['eTAG'], $etag); - $this->assertEquals($response_headers['eTaG'], $etag); - } - - public function testRequestURL() { - $test = new Test(); - $this->assertFalse(substr($test->server('request_uri', 'GET'), -1) === '?'); - $test = new Test(); - $this->assertFalse(substr($test->server('request_uri', 'POST'), -1) === '?'); - $test = new Test(); - $this->assertFalse(substr($test->server('request_uri', 'PUT'), -1) === '?'); - $test = new Test(); - $this->assertFalse(substr($test->server('request_uri', 'PATCH'), -1) === '?'); - $test = new Test(); - $this->assertFalse(substr($test->server('request_uri', 'DELETE'), -1) === '?'); - } - - public function testNestedData() { - $test = new Test(); - $data = array( - 'username' => 'myusername', - 'password' => 'mypassword', - 'more_data' => array( - 'param1' => 'something', - 'param2' => 'other thing', - 'another' => array( - 'extra' => 'level', - 'because' => 'I need it', - ), - ), - ); - $this->assertTrue( - $test->server('post', 'POST', $data) === http_build_query($data) - ); - } - - public function testPostContentTypes() { - $test = new Test(); - $test->server('server', 'POST', 'foo=bar'); - $this->assertEquals($test->curl->request_headers['Content-Type'], 'application/x-www-form-urlencoded'); - - $test = new Test(); - $test->server('server', 'POST', array( - 'foo' => 'bar', - )); - $this->assertEquals($test->curl->request_headers['Expect'], '100-continue'); - preg_match('/^multipart\/form-data; boundary=/', $test->curl->request_headers['Content-Type'], $content_type); - $this->assertTrue(!empty($content_type)); - } - - public function testJSONResponse() { - function assertion($key, $value) { - $test = new Test(); - $test->server('json_response', 'POST', array( - 'key' => $key, - 'value' => $value, - )); - - $response = $test->curl->response; - PHPUnit_Framework_Assert::assertNotNull($response); - PHPUnit_Framework_Assert::assertNull($response->null); - PHPUnit_Framework_Assert::assertTrue($response->true); - PHPUnit_Framework_Assert::assertFalse($response->false); - PHPUnit_Framework_Assert::assertTrue(is_int($response->integer)); - PHPUnit_Framework_Assert::assertTrue(is_float($response->float)); - PHPUnit_Framework_Assert::assertEmpty($response->empty); - PHPUnit_Framework_Assert::assertTrue(is_string($response->string)); - } - - assertion('Content-Type', 'application/json; charset=utf-8'); - assertion('content-type', 'application/json; charset=utf-8'); - assertion('Content-Type', 'application/json'); - assertion('content-type', 'application/json'); - assertion('CONTENT-TYPE', 'application/json'); - assertion('CONTENT-TYPE', 'APPLICATION/JSON'); - } - - public function testArrayToStringConversion() { - $test = new Test(); - $test->server('post', 'POST', array( - 'foo' => 'bar', - 'baz' => array( - ), - )); - $this->assertTrue($test->curl->response === 'foo=bar&baz='); - - $test = new Test(); - $test->server('post', 'POST', array( - 'foo' => 'bar', - 'baz' => array( - 'qux' => array( - ), - ), - )); - $this->assertTrue(urldecode($test->curl->response) === - 'foo=bar&baz[qux]=' - ); - - $test = new Test(); - $test->server('post', 'POST', array( - 'foo' => 'bar', - 'baz' => array( - 'qux' => array( - ), - 'wibble' => 'wobble', - ), - )); - $this->assertTrue(urldecode($test->curl->response) === - 'foo=bar&baz[qux]=&baz[wibble]=wobble' - ); - } - - public function testParallelRequests() { - $test = new Test(); - $curl = $test->curl; - $curl->beforeSend(function($instance) { - $instance->setHeader('X-DEBUG-TEST', 'request_uri'); - }); - $curl->get(array( - Test::TEST_URL . 'a/', - Test::TEST_URL . 'b/', - Test::TEST_URL . 'c/', - ), array( - 'foo' => 'bar', - )); - - $len = strlen('/a/?foo=bar'); - $this->assertTrue(substr($curl->curls['0']->response, - $len) === '/a/?foo=bar'); - $this->assertTrue(substr($curl->curls['1']->response, - $len) === '/b/?foo=bar'); - $this->assertTrue(substr($curl->curls['2']->response, - $len) === '/c/?foo=bar'); - } - - public function testParallelSetOptions() { - $test = new Test(); - $curl = $test->curl; - $curl->setHeader('X-DEBUG-TEST', 'server'); - $curl->setOpt(CURLOPT_USERAGENT, 'useragent'); - $curl->complete(function($instance) { - PHPUnit_Framework_Assert::assertTrue($instance->response === 'useragent'); - }); - $curl->get(array( - Test::TEST_URL, - ), array( - 'key' => 'HTTP_USER_AGENT', - )); - } - - public function testSuccessCallback() { - $success_called = false; - $error_called = false; - $complete_called = false; - - $test = new Test(); - $curl = $test->curl; - $curl->setHeader('X-DEBUG-TEST', 'get'); - - $curl->success(function($instance) use (&$success_called, &$error_called, &$complete_called) { - PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance); - PHPUnit_Framework_Assert::assertFalse($success_called); - PHPUnit_Framework_Assert::assertFalse($error_called); - PHPUnit_Framework_Assert::assertFalse($complete_called); - $success_called = true; - }); - $curl->error(function($instance) use (&$success_called, &$error_called, &$complete_called, &$curl) { - PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance); - PHPUnit_Framework_Assert::assertFalse($success_called); - PHPUnit_Framework_Assert::assertFalse($error_called); - PHPUnit_Framework_Assert::assertFalse($complete_called); - $error_called = true; - }); - $curl->complete(function($instance) use (&$success_called, &$error_called, &$complete_called) { - PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance); - PHPUnit_Framework_Assert::assertTrue($success_called); - PHPUnit_Framework_Assert::assertFalse($error_called); - PHPUnit_Framework_Assert::assertFalse($complete_called); - $complete_called = true; - }); - - $curl->get(Test::TEST_URL); - - $this->assertTrue($success_called); - $this->assertFalse($error_called); - $this->assertTrue($complete_called); - } - - public function testParallelSuccessCallback() { - $success_called = false; - $error_called = false; - $complete_called = false; - - $success_called_once = false; - $error_called_once = false; - $complete_called_once = false; - - $test = new Test(); - $curl = $test->curl; - $curl->setHeader('X-DEBUG-TEST', 'get'); - - $curl->success(function($instance) use (&$success_called, - &$error_called, - &$complete_called, - &$success_called_once) { - PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance); - PHPUnit_Framework_Assert::assertFalse($success_called); - PHPUnit_Framework_Assert::assertFalse($error_called); - PHPUnit_Framework_Assert::assertFalse($complete_called); - $success_called = true; - $success_called_once = true; - }); - $curl->error(function($instance) use (&$success_called, - &$error_called, - &$complete_called, - &$curl, - &$error_called_once) { - PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance); - PHPUnit_Framework_Assert::assertFalse($success_called); - PHPUnit_Framework_Assert::assertFalse($error_called); - PHPUnit_Framework_Assert::assertFalse($complete_called); - $error_called = true; - $error_called_once = true; - }); - $curl->complete(function($instance) use (&$success_called, - &$error_called, - &$complete_called, - &$complete_called_once) { - PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance); - PHPUnit_Framework_Assert::assertTrue($success_called); - PHPUnit_Framework_Assert::assertFalse($error_called); - PHPUnit_Framework_Assert::assertFalse($complete_called); - $complete_called = true; - $complete_called_once = true; - - PHPUnit_Framework_Assert::assertTrue($success_called); - PHPUnit_Framework_Assert::assertFalse($error_called); - PHPUnit_Framework_Assert::assertTrue($complete_called); - - $success_called = false; - $error_called = false; - $complete_called = false; - }); - - $curl->get(array( - Test::TEST_URL . 'a/', - Test::TEST_URL . 'b/', - Test::TEST_URL . 'c/', - )); - - PHPUnit_Framework_Assert::assertTrue($success_called_once || $error_called_once); - PHPUnit_Framework_Assert::assertTrue($complete_called_once); - } - - public function testErrorCallback() { - $success_called = false; - $error_called = false; - $complete_called = false; - - $test = new Test(); - $curl = $test->curl; - $curl->setHeader('X-DEBUG-TEST', 'get'); - $curl->setOpt(CURLOPT_CONNECTTIMEOUT_MS, 2000); - - $curl->success(function($instance) use (&$success_called, &$error_called, &$complete_called) { - PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance); - PHPUnit_Framework_Assert::assertFalse($success_called); - PHPUnit_Framework_Assert::assertFalse($error_called); - PHPUnit_Framework_Assert::assertFalse($complete_called); - $success_called = true; - }); - $curl->error(function($instance) use (&$success_called, &$error_called, &$complete_called, &$curl) { - PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance); - PHPUnit_Framework_Assert::assertFalse($success_called); - PHPUnit_Framework_Assert::assertFalse($error_called); - PHPUnit_Framework_Assert::assertFalse($complete_called); - $error_called = true; - }); - $curl->complete(function($instance) use (&$success_called, &$error_called, &$complete_called) { - PHPUnit_Framework_Assert::assertInstanceOf('Curl', $instance); - PHPUnit_Framework_Assert::assertFalse($success_called); - PHPUnit_Framework_Assert::assertTrue($error_called); - PHPUnit_Framework_Assert::assertFalse($complete_called); - $complete_called = true; - }); - - $curl->get(Test::ERROR_URL); - - $this->assertFalse($success_called); - $this->assertTrue($error_called); - $this->assertTrue($complete_called); - } - - public function testClose() { - $test = new Test(); - $curl = $test->curl; - $curl->setHeader('X-DEBUG-TEST', 'post'); - $curl->post(Test::TEST_URL); - $this->assertTrue(is_resource($curl->curl)); - $curl->close(); - $this->assertFalse(is_resource($curl->curl)); - } - - /** - * @expectedException PHPUnit_Framework_Error_Warning - */ - public function testRequiredOptionCurlInfoHeaderOutEmitsWarning() { - $curl = new Curl(); - $curl->setOpt(CURLINFO_HEADER_OUT, false); - } - - /** - * @expectedException PHPUnit_Framework_Error_Warning - */ - public function testRequiredOptionCurlOptHeaderEmitsWarning() { - $curl = new Curl(); - $curl->setOpt(CURLOPT_HEADER, false); - } - - /** - * @expectedException PHPUnit_Framework_Error_Warning - */ - public function testRequiredOptionCurlOptReturnTransferEmitsWarning() { - $curl = new Curl(); - $curl->setOpt(CURLOPT_RETURNTRANSFER, false); - } - - public function testRequestMethodSuccessiveGetRequests() { - $test = new Test(); - test($test, 'GET', 'POST'); - test($test, 'GET', 'PUT'); - test($test, 'GET', 'PATCH'); - test($test, 'GET', 'DELETE'); - test($test, 'GET', 'HEAD'); - test($test, 'GET', 'OPTIONS'); - } - - public function testRequestMethodSuccessivePostRequests() { - $test = new Test(); - test($test, 'POST', 'GET'); - test($test, 'POST', 'PUT'); - test($test, 'POST', 'PATCH'); - test($test, 'POST', 'DELETE'); - test($test, 'POST', 'HEAD'); - test($test, 'POST', 'OPTIONS'); - } - - public function testRequestMethodSuccessivePutRequests() { - $test = new Test(); - test($test, 'PUT', 'GET'); - test($test, 'PUT', 'POST'); - test($test, 'PUT', 'PATCH'); - test($test, 'PUT', 'DELETE'); - test($test, 'PUT', 'HEAD'); - test($test, 'PUT', 'OPTIONS'); - } - - public function testRequestMethodSuccessivePatchRequests() { - $test = new Test(); - test($test, 'PATCH', 'GET'); - test($test, 'PATCH', 'POST'); - test($test, 'PATCH', 'PUT'); - test($test, 'PATCH', 'DELETE'); - test($test, 'PATCH', 'HEAD'); - test($test, 'PATCH', 'OPTIONS'); - } - - public function testRequestMethodSuccessiveDeleteRequests() { - $test = new Test(); - test($test, 'DELETE', 'GET'); - test($test, 'DELETE', 'POST'); - test($test, 'DELETE', 'PUT'); - test($test, 'DELETE', 'PATCH'); - test($test, 'DELETE', 'HEAD'); - test($test, 'DELETE', 'OPTIONS'); - } - - public function testRequestMethodSuccessiveHeadRequests() { - $test = new Test(); - test($test, 'HEAD', 'GET'); - test($test, 'HEAD', 'POST'); - test($test, 'HEAD', 'PUT'); - test($test, 'HEAD', 'PATCH'); - test($test, 'HEAD', 'DELETE'); - test($test, 'HEAD', 'OPTIONS'); - } - - public function testRequestMethodSuccessiveOptionsRequests() { - $test = new Test(); - test($test, 'OPTIONS', 'GET'); - test($test, 'OPTIONS', 'POST'); - test($test, 'OPTIONS', 'PUT'); - test($test, 'OPTIONS', 'PATCH'); - test($test, 'OPTIONS', 'DELETE'); - test($test, 'OPTIONS', 'HEAD'); - } -} diff --git a/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/helper.inc.php b/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/helper.inc.php deleted file mode 100644 index 406a8e1..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/helper.inc.php +++ /dev/null @@ -1,47 +0,0 @@ -curl = new Curl(); - $this->curl->setOpt(CURLOPT_SSL_VERIFYPEER, false); - $this->curl->setOpt(CURLOPT_SSL_VERIFYHOST, false); - } - - function server($test, $request_method, $data=array()) { - $this->curl->setHeader('X-DEBUG-TEST', $test); - $request_method = strtolower($request_method); - $this->curl->$request_method(self::TEST_URL, $data); - return $this->curl->response; - } -} - -function test($instance, $before, $after) { - $instance->server('request_method', $before); - PHPUnit_Framework_Assert::assertEquals($instance->curl->response_headers['X-REQUEST-METHOD'], $before); - $instance->server('request_method', $after); - PHPUnit_Framework_Assert::assertEquals($instance->curl->response_headers['X-REQUEST-METHOD'], $after); -} - -function create_png() { - // PNG image data, 1 x 1, 1-bit colormap, non-interlaced - ob_start(); - imagepng(imagecreatefromstring(base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'))); - $raw_image = ob_get_contents(); - ob_end_clean(); - return $raw_image; -} - -function create_tmp_file($data) { - $tmp_file = tmpfile(); - fwrite($tmp_file, $data); - rewind($tmp_file); - return $tmp_file; -} - -function get_png() { - $tmp_filename = tempnam('/tmp', 'php-curl-class.'); - file_put_contents($tmp_filename, create_png()); - return $tmp_filename; -} diff --git a/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/index.php b/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/index.php deleted file mode 120000 index 21a87f6..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/index.php +++ /dev/null @@ -1 +0,0 @@ -server.php \ No newline at end of file diff --git a/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.hdf b/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.hdf deleted file mode 100644 index 8e49d88..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.hdf +++ /dev/null @@ -1,7 +0,0 @@ -Log { - Level = Verbose -} - -Server { - DefaultDocument = index.php -} diff --git a/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.php b/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.php deleted file mode 100644 index 96e835b..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/tests/PHPCurlClass/server.php +++ /dev/null @@ -1,132 +0,0 @@ - $_SERVER['PHP_AUTH_USER'], - 'password' => $_SERVER['PHP_AUTH_PW'], - )); - exit; -} -else if ($test === 'get') { - echo http_build_query($_GET); - exit; -} -else if ($test === 'post') { - echo http_build_query($_POST); - exit; -} -else if ($test === 'put') { - echo $http_raw_post_data; - exit; -} -else if ($test === 'post_multidimensional') { - echo $http_raw_post_data; - exit; -} -else if ($test === 'post_file_path_upload') { - echo mime_content_type($_FILES[$key]['tmp_name']); - exit; -} -else if ($test === 'put_file_handle') { - $tmp_filename = tempnam('/tmp', 'php-curl-class.'); - file_put_contents($tmp_filename, $http_raw_post_data); - echo mime_content_type($tmp_filename); - unlink($tmp_filename); - exit; -} -else if ($test === 'request_method') { - header('X-REQUEST-METHOD: ' . $request_method); - echo $request_method; - exit; -} -else if ($test === 'request_uri') { - echo $_SERVER['REQUEST_URI']; - exit; -} -else if ($test === 'cookiejar') { - setcookie('mycookie', 'yum'); - exit; -} -else if ($test === 'multiple_cookie') { - setcookie('cookie1', 'scrumptious'); - setcookie('cookie2', 'mouthwatering'); - exit; -} -else if ($test === 'response_header') { - header('Content-Type: application/json'); - header('ETag: ' . md5('worldpeace')); - exit; -} -else if ($test === 'json_response') { - $key = $_POST['key']; - $value = $_POST['value']; - header($key . ': ' . $value); - echo json_encode(array( - 'null' => null, - 'true' => true, - 'false' => false, - 'integer' => 1, - 'float' => 3.14, - 'empty' => '', - 'string' => 'string', - )); - exit; -} -else if ($test === 'error_message') { - if (function_exists('http_response_code')) { - http_response_code(401); - } - else { - header('HTTP/1.1 401 Unauthorized'); - } - exit; -} - -header('Content-Type: text/plain'); - -$data_mapping = array( - 'cookie' => '_COOKIE', - 'delete' => '_GET', - 'get' => '_GET', - 'patch' => '_PATCH', - 'post' => '_POST', - 'put' => '_PUT', - 'server' => '_SERVER', -); - -$data = $$data_mapping[$test]; -$value = isset($data[$key]) ? $data[$key] : ''; -echo $value; diff --git a/web/app/vendor/php-curl-class/php-curl-class/tests/phpunit.xml b/web/app/vendor/php-curl-class/php-curl-class/tests/phpunit.xml deleted file mode 100644 index 8eccd99..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/tests/phpunit.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - . - - - - - diff --git a/web/app/vendor/php-curl-class/php-curl-class/tests/run.sh b/web/app/vendor/php-curl-class/php-curl-class/tests/run.sh deleted file mode 100644 index 54330ba..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/tests/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -php -S 127.0.0.1:8000 -t PHPCurlClass/ & -pid=$! -phpunit --configuration phpunit.xml -kill $pid diff --git a/web/app/vendor/php-curl-class/php-curl-class/tests/syntax.sh b/web/app/vendor/php-curl-class/php-curl-class/tests/syntax.sh deleted file mode 100644 index a5a32f1..0000000 --- a/web/app/vendor/php-curl-class/php-curl-class/tests/syntax.sh +++ /dev/null @@ -1 +0,0 @@ -phpcs --standard=PSR2 ../src/Curl.class.php