composer-setup.php 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. setupEnvironment();
  12. process(is_array($argv) ? $argv : array());
  13. /**
  14. * Initializes various values
  15. *
  16. * @throws RuntimeException If uopz extension prevents exit calls
  17. */
  18. function setupEnvironment()
  19. {
  20. ini_set('display_errors', 1);
  21. if (extension_loaded('uopz') && !(ini_get('uopz.disable') || ini_get('uopz.exit'))) {
  22. // uopz works at opcode level and disables exit calls
  23. if (function_exists('uopz_allow_exit')) {
  24. @uopz_allow_exit(true);
  25. } else {
  26. throw new RuntimeException('The uopz extension ignores exit calls and breaks this installer.');
  27. }
  28. }
  29. $installer = 'ComposerInstaller';
  30. if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
  31. if ($version = getenv('COMPOSERSETUP')) {
  32. $installer = sprintf('Composer-Setup.exe/%s', $version);
  33. }
  34. }
  35. define('COMPOSER_INSTALLER', $installer);
  36. }
  37. /**
  38. * Processes the installer
  39. */
  40. function process($argv)
  41. {
  42. // Determine ANSI output from --ansi and --no-ansi flags
  43. setUseAnsi($argv);
  44. $help = in_array('--help', $argv) || in_array('-h', $argv);
  45. if ($help) {
  46. displayHelp();
  47. exit(0);
  48. }
  49. $check = in_array('--check', $argv);
  50. $force = in_array('--force', $argv);
  51. $quiet = in_array('--quiet', $argv);
  52. $channel = 'stable';
  53. if (in_array('--snapshot', $argv)) {
  54. $channel = 'snapshot';
  55. } elseif (in_array('--preview', $argv)) {
  56. $channel = 'preview';
  57. } elseif (in_array('--1', $argv)) {
  58. $channel = '1';
  59. } elseif (in_array('--2', $argv)) {
  60. $channel = '2';
  61. } elseif (in_array('--2.2', $argv)) {
  62. $channel = '2.2';
  63. }
  64. $disableTls = in_array('--disable-tls', $argv);
  65. $installDir = getOptValue('--install-dir', $argv, false);
  66. $version = getOptValue('--version', $argv, false);
  67. $filename = getOptValue('--filename', $argv, 'composer.phar');
  68. $cafile = getOptValue('--cafile', $argv, false);
  69. if (!checkParams($installDir, $version, $cafile)) {
  70. exit(1);
  71. }
  72. $ok = checkPlatform($warnings, $quiet, $disableTls, true);
  73. if ($check) {
  74. // Only show warnings if we haven't output any errors
  75. if ($ok) {
  76. showWarnings($warnings);
  77. showSecurityWarning($disableTls);
  78. }
  79. exit($ok ? 0 : 1);
  80. }
  81. if ($ok || $force) {
  82. if ($channel === '1' && !$quiet) {
  83. out('Warning: You forced the install of Composer 1.x via --1, but Composer 2.x is the latest stable version. Updating to it via composer self-update --stable is recommended.', 'error');
  84. }
  85. $installer = new Installer($quiet, $disableTls, $cafile);
  86. if ($installer->run($version, $installDir, $filename, $channel)) {
  87. showWarnings($warnings);
  88. showSecurityWarning($disableTls);
  89. exit(0);
  90. }
  91. }
  92. exit(1);
  93. }
  94. /**
  95. * Displays the help
  96. */
  97. function displayHelp()
  98. {
  99. echo <<<EOF
  100. Composer Installer
  101. ------------------
  102. Options
  103. --help this help
  104. --check for checking environment only
  105. --force forces the installation
  106. --ansi force ANSI color output
  107. --no-ansi disable ANSI color output
  108. --quiet do not output unimportant messages
  109. --install-dir="..." accepts a target installation directory
  110. --preview install the latest version from the preview (alpha/beta/rc) channel instead of stable
  111. --snapshot install the latest version from the snapshot (dev builds) channel instead of stable
  112. --1 install the latest stable Composer 1.x (EOL) version
  113. --2 install the latest stable Composer 2.x version
  114. --2.2 install the latest stable Composer 2.2.x (LTS) version
  115. --version="..." accepts a specific version to install instead of the latest
  116. --filename="..." accepts a target filename (default: composer.phar)
  117. --disable-tls disable SSL/TLS security for file downloads
  118. --cafile="..." accepts a path to a Certificate Authority (CA) certificate file for SSL/TLS verification
  119. EOF;
  120. }
  121. /**
  122. * Sets the USE_ANSI define for colorizing output
  123. *
  124. * @param array $argv Command-line arguments
  125. */
  126. function setUseAnsi($argv)
  127. {
  128. // --no-ansi wins over --ansi
  129. if (in_array('--no-ansi', $argv)) {
  130. define('USE_ANSI', false);
  131. } elseif (in_array('--ansi', $argv)) {
  132. define('USE_ANSI', true);
  133. } else {
  134. define('USE_ANSI', outputSupportsColor());
  135. }
  136. }
  137. /**
  138. * Returns whether color output is supported
  139. *
  140. * @return bool
  141. */
  142. function outputSupportsColor()
  143. {
  144. if (false !== getenv('NO_COLOR') || !defined('STDOUT')) {
  145. return false;
  146. }
  147. if ('Hyper' === getenv('TERM_PROGRAM')) {
  148. return true;
  149. }
  150. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  151. return (function_exists('sapi_windows_vt100_support')
  152. && sapi_windows_vt100_support(STDOUT))
  153. || false !== getenv('ANSICON')
  154. || 'ON' === getenv('ConEmuANSI')
  155. || 'xterm' === getenv('TERM');
  156. }
  157. if (function_exists('stream_isatty')) {
  158. return stream_isatty(STDOUT);
  159. }
  160. if (function_exists('posix_isatty')) {
  161. return posix_isatty(STDOUT);
  162. }
  163. $stat = fstat(STDOUT);
  164. // Check if formatted mode is S_IFCHR
  165. return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
  166. }
  167. /**
  168. * Returns the value of a command-line option
  169. *
  170. * @param string $opt The command-line option to check
  171. * @param array $argv Command-line arguments
  172. * @param mixed $default Default value to be returned
  173. *
  174. * @return mixed The command-line value or the default
  175. */
  176. function getOptValue($opt, $argv, $default)
  177. {
  178. $optLength = strlen($opt);
  179. foreach ($argv as $key => $value) {
  180. $next = $key + 1;
  181. if (0 === strpos($value, $opt)) {
  182. if ($optLength === strlen($value) && isset($argv[$next])) {
  183. return trim($argv[$next]);
  184. } else {
  185. return trim(substr($value, $optLength + 1));
  186. }
  187. }
  188. }
  189. return $default;
  190. }
  191. /**
  192. * Checks that user-supplied params are valid
  193. *
  194. * @param mixed $installDir The required istallation directory
  195. * @param mixed $version The required composer version to install
  196. * @param mixed $cafile Certificate Authority file
  197. *
  198. * @return bool True if the supplied params are okay
  199. */
  200. function checkParams($installDir, $version, $cafile)
  201. {
  202. $result = true;
  203. if (false !== $installDir && !is_dir($installDir)) {
  204. out("The defined install dir ({$installDir}) does not exist.", 'info');
  205. $result = false;
  206. }
  207. if (false !== $version && 1 !== preg_match('/^\d+\.\d+\.\d+(\-(alpha|beta|RC)\d*)*$/', $version)) {
  208. out("The defined install version ({$version}) does not match release pattern.", 'info');
  209. $result = false;
  210. }
  211. if (false !== $cafile && (!file_exists($cafile) || !is_readable($cafile))) {
  212. out("The defined Certificate Authority (CA) cert file ({$cafile}) does not exist or is not readable.", 'info');
  213. $result = false;
  214. }
  215. return $result;
  216. }
  217. /**
  218. * Checks the platform for possible issues running Composer
  219. *
  220. * Errors are written to the output, warnings are saved for later display.
  221. *
  222. * @param array $warnings Populated by method, to be shown later
  223. * @param bool $quiet Quiet mode
  224. * @param bool $disableTls Bypass tls
  225. * @param bool $install If we are installing, rather than diagnosing
  226. *
  227. * @return bool True if there are no errors
  228. */
  229. function checkPlatform(&$warnings, $quiet, $disableTls, $install)
  230. {
  231. getPlatformIssues($errors, $warnings, $install);
  232. // Make openssl warning an error if tls has not been specifically disabled
  233. if (isset($warnings['openssl']) && !$disableTls) {
  234. $errors['openssl'] = $warnings['openssl'];
  235. unset($warnings['openssl']);
  236. }
  237. if (!empty($errors)) {
  238. // Composer-Setup.exe uses "Some settings" to flag platform errors
  239. out('Some settings on your machine make Composer unable to work properly.', 'error');
  240. out('Make sure that you fix the issues listed below and run this script again:', 'error');
  241. outputIssues($errors);
  242. return false;
  243. }
  244. if (empty($warnings) && !$quiet) {
  245. out('All settings correct for using Composer', 'success');
  246. }
  247. return true;
  248. }
  249. /**
  250. * Checks platform configuration for common incompatibility issues
  251. *
  252. * @param array $errors Populated by method
  253. * @param array $warnings Populated by method
  254. * @param bool $install If we are installing, rather than diagnosing
  255. *
  256. * @return bool If any errors or warnings have been found
  257. */
  258. function getPlatformIssues(&$errors, &$warnings, $install)
  259. {
  260. $errors = array();
  261. $warnings = array();
  262. $iniMessage = PHP_EOL.getIniMessage();
  263. $iniMessage .= PHP_EOL.'If you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.';
  264. if (ini_get('detect_unicode')) {
  265. $errors['unicode'] = array(
  266. 'The detect_unicode setting must be disabled.',
  267. 'Add the following to the end of your `php.ini`:',
  268. ' detect_unicode = Off',
  269. $iniMessage
  270. );
  271. }
  272. if (extension_loaded('suhosin')) {
  273. $suhosin = ini_get('suhosin.executor.include.whitelist');
  274. $suhosinBlacklist = ini_get('suhosin.executor.include.blacklist');
  275. if (false === stripos($suhosin, 'phar') && (!$suhosinBlacklist || false !== stripos($suhosinBlacklist, 'phar'))) {
  276. $errors['suhosin'] = array(
  277. 'The suhosin.executor.include.whitelist setting is incorrect.',
  278. 'Add the following to the end of your `php.ini` or suhosin.ini (Example path [for Debian]: /etc/php5/cli/conf.d/suhosin.ini):',
  279. ' suhosin.executor.include.whitelist = phar '.$suhosin,
  280. $iniMessage
  281. );
  282. }
  283. }
  284. if (!function_exists('json_decode')) {
  285. $errors['json'] = array(
  286. 'The json extension is missing.',
  287. 'Install it or recompile php without --disable-json'
  288. );
  289. }
  290. if (!extension_loaded('Phar')) {
  291. $errors['phar'] = array(
  292. 'The phar extension is missing.',
  293. 'Install it or recompile php without --disable-phar'
  294. );
  295. }
  296. if (!extension_loaded('filter')) {
  297. $errors['filter'] = array(
  298. 'The filter extension is missing.',
  299. 'Install it or recompile php without --disable-filter'
  300. );
  301. }
  302. if (!extension_loaded('hash')) {
  303. $errors['hash'] = array(
  304. 'The hash extension is missing.',
  305. 'Install it or recompile php without --disable-hash'
  306. );
  307. }
  308. if (!extension_loaded('iconv') && !extension_loaded('mbstring')) {
  309. $errors['iconv_mbstring'] = array(
  310. 'The iconv OR mbstring extension is required and both are missing.',
  311. 'Install either of them or recompile php without --disable-iconv'
  312. );
  313. }
  314. if (!ini_get('allow_url_fopen')) {
  315. $errors['allow_url_fopen'] = array(
  316. 'The allow_url_fopen setting is incorrect.',
  317. 'Add the following to the end of your `php.ini`:',
  318. ' allow_url_fopen = On',
  319. $iniMessage
  320. );
  321. }
  322. if (extension_loaded('ionCube Loader') && ioncube_loader_iversion() < 40009) {
  323. $ioncube = ioncube_loader_version();
  324. $errors['ioncube'] = array(
  325. 'Your ionCube Loader extension ('.$ioncube.') is incompatible with Phar files.',
  326. 'Upgrade to ionCube 4.0.9 or higher or remove this line (path may be different) from your `php.ini` to disable it:',
  327. ' zend_extension = /usr/lib/php5/20090626+lfs/ioncube_loader_lin_5.3.so',
  328. $iniMessage
  329. );
  330. }
  331. if (version_compare(PHP_VERSION, '5.3.2', '<')) {
  332. $errors['php'] = array(
  333. 'Your PHP ('.PHP_VERSION.') is too old, you must upgrade to PHP 5.3.2 or higher.'
  334. );
  335. }
  336. if (version_compare(PHP_VERSION, '5.3.4', '<')) {
  337. $warnings['php'] = array(
  338. 'Your PHP ('.PHP_VERSION.') is quite old, upgrading to PHP 5.3.4 or higher is recommended.',
  339. 'Composer works with 5.3.2+ for most people, but there might be edge case issues.'
  340. );
  341. }
  342. if (!extension_loaded('openssl')) {
  343. $warnings['openssl'] = array(
  344. 'The openssl extension is missing, which means that secure HTTPS transfers are impossible.',
  345. 'If possible you should enable it or recompile php with --with-openssl'
  346. );
  347. }
  348. if (extension_loaded('openssl') && OPENSSL_VERSION_NUMBER < 0x1000100f) {
  349. // Attempt to parse version number out, fallback to whole string value.
  350. $opensslVersion = trim(strstr(OPENSSL_VERSION_TEXT, ' '));
  351. $opensslVersion = substr($opensslVersion, 0, strpos($opensslVersion, ' '));
  352. $opensslVersion = $opensslVersion ? $opensslVersion : OPENSSL_VERSION_TEXT;
  353. $warnings['openssl_version'] = array(
  354. 'The OpenSSL library ('.$opensslVersion.') used by PHP does not support TLSv1.2 or TLSv1.1.',
  355. 'If possible you should upgrade OpenSSL to version 1.0.1 or above.'
  356. );
  357. }
  358. if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) {
  359. $warnings['apc_cli'] = array(
  360. 'The apc.enable_cli setting is incorrect.',
  361. 'Add the following to the end of your `php.ini`:',
  362. ' apc.enable_cli = Off',
  363. $iniMessage
  364. );
  365. }
  366. if (!$install && extension_loaded('xdebug')) {
  367. $warnings['xdebug_loaded'] = array(
  368. 'The xdebug extension is loaded, this can slow down Composer a little.',
  369. 'Disabling it when using Composer is recommended.'
  370. );
  371. if (ini_get('xdebug.profiler_enabled')) {
  372. $warnings['xdebug_profile'] = array(
  373. 'The xdebug.profiler_enabled setting is enabled, this can slow down Composer a lot.',
  374. 'Add the following to the end of your `php.ini` to disable it:',
  375. ' xdebug.profiler_enabled = 0',
  376. $iniMessage
  377. );
  378. }
  379. }
  380. if (!extension_loaded('zlib')) {
  381. $warnings['zlib'] = array(
  382. 'The zlib extension is not loaded, this can slow down Composer a lot.',
  383. 'If possible, install it or recompile php with --with-zlib',
  384. $iniMessage
  385. );
  386. }
  387. if (defined('PHP_WINDOWS_VERSION_BUILD')
  388. && (version_compare(PHP_VERSION, '7.2.23', '<')
  389. || (version_compare(PHP_VERSION, '7.3.0', '>=')
  390. && version_compare(PHP_VERSION, '7.3.10', '<')))) {
  391. $warnings['onedrive'] = array(
  392. 'The Windows OneDrive folder is not supported on PHP versions below 7.2.23 and 7.3.10.',
  393. 'Upgrade your PHP ('.PHP_VERSION.') to use this location with Composer.'
  394. );
  395. }
  396. if (extension_loaded('uopz') && !(ini_get('uopz.disable') || ini_get('uopz.exit'))) {
  397. $warnings['uopz'] = array(
  398. 'The uopz extension ignores exit calls and may not work with all Composer commands.',
  399. 'Disabling it when using Composer is recommended.'
  400. );
  401. }
  402. ob_start();
  403. phpinfo(INFO_GENERAL);
  404. $phpinfo = (string) ob_get_clean();
  405. if (preg_match('{Configure Command(?: *</td><td class="v">| *=> *)(.*?)(?:</td>|$)}m', $phpinfo, $match)) {
  406. $configure = $match[1];
  407. if (false !== strpos($configure, '--enable-sigchild')) {
  408. $warnings['sigchild'] = array(
  409. 'PHP was compiled with --enable-sigchild which can cause issues on some platforms.',
  410. 'Recompile it without this flag if possible, see also:',
  411. ' https://bugs.php.net/bug.php?id=22999'
  412. );
  413. }
  414. if (false !== strpos($configure, '--with-curlwrappers')) {
  415. $warnings['curlwrappers'] = array(
  416. 'PHP was compiled with --with-curlwrappers which will cause issues with HTTP authentication and GitHub.',
  417. 'Recompile it without this flag if possible'
  418. );
  419. }
  420. }
  421. // Stringify the message arrays
  422. foreach ($errors as $key => $value) {
  423. $errors[$key] = PHP_EOL.implode(PHP_EOL, $value);
  424. }
  425. foreach ($warnings as $key => $value) {
  426. $warnings[$key] = PHP_EOL.implode(PHP_EOL, $value);
  427. }
  428. return !empty($errors) || !empty($warnings);
  429. }
  430. /**
  431. * Outputs an array of issues
  432. *
  433. * @param array $issues
  434. */
  435. function outputIssues($issues)
  436. {
  437. foreach ($issues as $issue) {
  438. out($issue, 'info');
  439. }
  440. out('');
  441. }
  442. /**
  443. * Outputs any warnings found
  444. *
  445. * @param array $warnings
  446. */
  447. function showWarnings($warnings)
  448. {
  449. if (!empty($warnings)) {
  450. out('Some settings on your machine may cause stability issues with Composer.', 'error');
  451. out('If you encounter issues, try to change the following:', 'error');
  452. outputIssues($warnings);
  453. }
  454. }
  455. /**
  456. * Outputs an end of process warning if tls has been bypassed
  457. *
  458. * @param bool $disableTls Bypass tls
  459. */
  460. function showSecurityWarning($disableTls)
  461. {
  462. if ($disableTls) {
  463. out('You have instructed the Installer not to enforce SSL/TLS security on remote HTTPS requests.', 'info');
  464. out('This will leave all downloads during installation vulnerable to Man-In-The-Middle (MITM) attacks', 'info');
  465. }
  466. }
  467. /**
  468. * colorize output
  469. */
  470. function out($text, $color = null, $newLine = true)
  471. {
  472. $styles = array(
  473. 'success' => "\033[0;32m%s\033[0m",
  474. 'error' => "\033[31;31m%s\033[0m",
  475. 'info' => "\033[33;33m%s\033[0m"
  476. );
  477. $format = '%s';
  478. if (is_string($color) && isset($styles[$color]) && USE_ANSI) {
  479. $format = $styles[$color];
  480. }
  481. if ($newLine) {
  482. $format .= PHP_EOL;
  483. }
  484. printf($format, $text);
  485. }
  486. /**
  487. * Returns the system-dependent Composer home location, which may not exist
  488. *
  489. * @return string
  490. */
  491. function getHomeDir()
  492. {
  493. $home = getenv('COMPOSER_HOME');
  494. if ($home) {
  495. return $home;
  496. }
  497. $userDir = getUserDir();
  498. if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
  499. return $userDir.'/Composer';
  500. }
  501. $dirs = array();
  502. if (useXdg()) {
  503. // XDG Base Directory Specifications
  504. $xdgConfig = getenv('XDG_CONFIG_HOME');
  505. if (!$xdgConfig) {
  506. $xdgConfig = $userDir . '/.config';
  507. }
  508. $dirs[] = $xdgConfig . '/composer';
  509. }
  510. $dirs[] = $userDir . '/.composer';
  511. // select first dir which exists of: $XDG_CONFIG_HOME/composer or ~/.composer
  512. foreach ($dirs as $dir) {
  513. if (is_dir($dir)) {
  514. return $dir;
  515. }
  516. }
  517. // if none exists, we default to first defined one (XDG one if system uses it, or ~/.composer otherwise)
  518. return $dirs[0];
  519. }
  520. /**
  521. * Returns the location of the user directory from the environment
  522. * @throws RuntimeException If the environment value does not exists
  523. *
  524. * @return string
  525. */
  526. function getUserDir()
  527. {
  528. $userEnv = defined('PHP_WINDOWS_VERSION_MAJOR') ? 'APPDATA' : 'HOME';
  529. $userDir = getenv($userEnv);
  530. if (!$userDir) {
  531. throw new RuntimeException('The '.$userEnv.' or COMPOSER_HOME environment variable must be set for composer to run correctly');
  532. }
  533. return rtrim(strtr($userDir, '\\', '/'), '/');
  534. }
  535. /**
  536. * @return bool
  537. */
  538. function useXdg()
  539. {
  540. foreach (array_keys($_SERVER) as $key) {
  541. if (strpos((string) $key, 'XDG_') === 0) {
  542. return true;
  543. }
  544. }
  545. if (is_dir('/etc/xdg')) {
  546. return true;
  547. }
  548. return false;
  549. }
  550. function validateCaFile($contents)
  551. {
  552. // assume the CA is valid if php is vulnerable to
  553. // https://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
  554. if (
  555. PHP_VERSION_ID <= 50327
  556. || (PHP_VERSION_ID >= 50400 && PHP_VERSION_ID < 50422)
  557. || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50506)
  558. ) {
  559. return !empty($contents);
  560. }
  561. return (bool) openssl_x509_parse($contents);
  562. }
  563. /**
  564. * Returns php.ini location information
  565. *
  566. * @return string
  567. */
  568. function getIniMessage()
  569. {
  570. $paths = array((string) php_ini_loaded_file());
  571. $scanned = php_ini_scanned_files();
  572. if ($scanned !== false) {
  573. $paths = array_merge($paths, array_map('trim', explode(',', $scanned)));
  574. }
  575. // We will have at least one value, which may be empty
  576. if ($paths[0] === '') {
  577. array_shift($paths);
  578. }
  579. $ini = array_shift($paths);
  580. if ($ini === null) {
  581. return 'A php.ini file does not exist. You will have to create one.';
  582. }
  583. if (count($paths) > 1) {
  584. return 'Your command-line PHP is using multiple ini files. Run `php --ini` to show them.';
  585. }
  586. return 'The php.ini used by your command-line PHP is: '.$ini;
  587. }
  588. class Installer
  589. {
  590. private $quiet;
  591. private $disableTls;
  592. private $cafile;
  593. private $displayPath;
  594. private $target;
  595. private $tmpFile;
  596. private $tmpCafile;
  597. private $baseUrl;
  598. private $algo;
  599. private $errHandler;
  600. private $httpClient;
  601. private $pubKeys = array();
  602. private $installs = array();
  603. /**
  604. * Constructor - must not do anything that throws an exception
  605. *
  606. * @param bool $quiet Quiet mode
  607. * @param bool $disableTls Bypass tls
  608. * @param mixed $cafile Path to CA bundle, or false
  609. */
  610. public function __construct($quiet, $disableTls, $caFile)
  611. {
  612. if (($this->quiet = $quiet)) {
  613. ob_start();
  614. }
  615. $this->disableTls = $disableTls;
  616. $this->cafile = $caFile;
  617. $this->errHandler = new ErrorHandler();
  618. }
  619. /**
  620. * Runs the installer
  621. *
  622. * @param mixed $version Specific version to install, or false
  623. * @param mixed $installDir Specific installation directory, or false
  624. * @param string $filename Specific filename to save to, or composer.phar
  625. * @param string $channel Specific version channel to use
  626. * @throws Exception If anything other than a RuntimeException is caught
  627. *
  628. * @return bool If the installation succeeded
  629. */
  630. public function run($version, $installDir, $filename, $channel)
  631. {
  632. try {
  633. $this->initTargets($installDir, $filename);
  634. $this->initTls();
  635. $this->httpClient = new HttpClient($this->disableTls, $this->cafile);
  636. $result = $this->install($version, $channel);
  637. // in case --1 or --2 is passed, we leave the default channel for next self-update to stable
  638. if (1 === preg_match('{^\d+$}D', $channel)) {
  639. $channel = 'stable';
  640. }
  641. if ($result && $channel !== 'stable' && !$version && defined('PHP_BINARY')) {
  642. $null = (defined('PHP_WINDOWS_VERSION_MAJOR') ? 'NUL' : '/dev/null');
  643. @exec(escapeshellarg(PHP_BINARY) .' '.escapeshellarg($this->target).' self-update --'.$channel.' --set-channel-only -q > '.$null.' 2> '.$null, $output);
  644. }
  645. } catch (Exception $e) {
  646. $result = false;
  647. }
  648. // Always clean up
  649. $this->cleanUp($result);
  650. if (isset($e)) {
  651. // Rethrow anything that is not a RuntimeException
  652. if (!$e instanceof RuntimeException) {
  653. throw $e;
  654. }
  655. out($e->getMessage(), 'error');
  656. }
  657. return $result;
  658. }
  659. /**
  660. * Initialization methods to set the required filenames and composer url
  661. *
  662. * @param mixed $installDir Specific installation directory, or false
  663. * @param string $filename Specific filename to save to, or composer.phar
  664. * @throws RuntimeException If the installation directory is not writable
  665. */
  666. protected function initTargets($installDir, $filename)
  667. {
  668. $this->displayPath = ($installDir ? rtrim($installDir, '/').'/' : '').$filename;
  669. $installDir = $installDir ? realpath($installDir) : getcwd();
  670. if (!is_writeable($installDir)) {
  671. throw new RuntimeException('The installation directory "'.$installDir.'" is not writable');
  672. }
  673. $this->target = $installDir.DIRECTORY_SEPARATOR.$filename;
  674. $this->tmpFile = $installDir.DIRECTORY_SEPARATOR.basename($this->target, '.phar').'-temp.phar';
  675. $uriScheme = $this->disableTls ? 'http' : 'https';
  676. $this->baseUrl = $uriScheme.'://getcomposer.org';
  677. }
  678. /**
  679. * A wrapper around methods to check tls and write public keys
  680. * @throws RuntimeException If SHA384 is not supported
  681. */
  682. protected function initTls()
  683. {
  684. if ($this->disableTls) {
  685. return;
  686. }
  687. if (!in_array('sha384', array_map('strtolower', openssl_get_md_methods()))) {
  688. throw new RuntimeException('SHA384 is not supported by your openssl extension');
  689. }
  690. $this->algo = defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'SHA384';
  691. $home = $this->getComposerHome();
  692. $this->pubKeys = array(
  693. 'dev' => $this->installKey(self::getPKDev(), $home, 'keys.dev.pub'),
  694. 'tags' => $this->installKey(self::getPKTags(), $home, 'keys.tags.pub')
  695. );
  696. if (empty($this->cafile) && !HttpClient::getSystemCaRootBundlePath()) {
  697. $this->cafile = $this->tmpCafile = $this->installKey(HttpClient::getPackagedCaFile(), $home, 'cacert-temp.pem');
  698. }
  699. }
  700. /**
  701. * Returns the Composer home directory, creating it if required
  702. * @throws RuntimeException If the directory cannot be created
  703. *
  704. * @return string
  705. */
  706. protected function getComposerHome()
  707. {
  708. $home = getHomeDir();
  709. if (!is_dir($home)) {
  710. $this->errHandler->start();
  711. if (!mkdir($home, 0777, true)) {
  712. throw new RuntimeException(sprintf(
  713. 'Unable to create Composer home directory "%s": %s',
  714. $home,
  715. $this->errHandler->message
  716. ));
  717. }
  718. $this->installs[] = $home;
  719. $this->errHandler->stop();
  720. }
  721. return $home;
  722. }
  723. /**
  724. * Writes public key data to disc
  725. *
  726. * @param string $data The public key(s) in pem format
  727. * @param string $path The directory to write to
  728. * @param string $filename The name of the file
  729. * @throws RuntimeException If the file cannot be written
  730. *
  731. * @return string The path to the saved data
  732. */
  733. protected function installKey($data, $path, $filename)
  734. {
  735. $this->errHandler->start();
  736. $target = $path.DIRECTORY_SEPARATOR.$filename;
  737. $installed = file_exists($target);
  738. $write = file_put_contents($target, $data, LOCK_EX);
  739. @chmod($target, 0644);
  740. $this->errHandler->stop();
  741. if (!$write) {
  742. throw new RuntimeException(sprintf('Unable to write %s to: %s', $filename, $path));
  743. }
  744. if (!$installed) {
  745. $this->installs[] = $target;
  746. }
  747. return $target;
  748. }
  749. /**
  750. * The main install function
  751. *
  752. * @param mixed $version Specific version to install, or false
  753. * @param string $channel Version channel to use
  754. *
  755. * @return bool If the installation succeeded
  756. */
  757. protected function install($version, $channel)
  758. {
  759. $retries = 3;
  760. $result = false;
  761. $infoMsg = 'Downloading...';
  762. $infoType = 'info';
  763. while ($retries--) {
  764. if (!$this->quiet) {
  765. out($infoMsg, $infoType);
  766. $infoMsg = 'Retrying...';
  767. $infoType = 'error';
  768. }
  769. if (!$this->getVersion($channel, $version, $url, $error)) {
  770. out($error, 'error');
  771. continue;
  772. }
  773. if (!$this->downloadToTmp($url, $signature, $error)) {
  774. out($error, 'error');
  775. continue;
  776. }
  777. if (!$this->verifyAndSave($version, $signature, $error)) {
  778. out($error, 'error');
  779. continue;
  780. }
  781. $result = true;
  782. break;
  783. }
  784. if (!$this->quiet) {
  785. if ($result) {
  786. out(PHP_EOL."Composer (version {$version}) successfully installed to: {$this->target}", 'success');
  787. out("Use it: php {$this->displayPath}", 'info');
  788. out('');
  789. } else {
  790. out('The download failed repeatedly, aborting.', 'error');
  791. }
  792. }
  793. return $result;
  794. }
  795. /**
  796. * Sets the version url, downloading version data if required
  797. *
  798. * @param string $channel Version channel to use
  799. * @param false|string $version Version to install, or set by method
  800. * @param null|string $url The versioned url, set by method
  801. * @param null|string $error Set by method on failure
  802. *
  803. * @return bool If the operation succeeded
  804. */
  805. protected function getVersion($channel, &$version, &$url, &$error)
  806. {
  807. $error = '';
  808. if ($version) {
  809. if (empty($url)) {
  810. $url = $this->baseUrl."/download/{$version}/composer.phar";
  811. }
  812. return true;
  813. }
  814. $this->errHandler->start();
  815. if ($this->downloadVersionData($data, $error)) {
  816. $this->parseVersionData($data, $channel, $version, $url);
  817. }
  818. $this->errHandler->stop();
  819. return empty($error);
  820. }
  821. /**
  822. * Downloads and json-decodes version data
  823. *
  824. * @param null|array $data Downloaded version data, set by method
  825. * @param null|string $error Set by method on failure
  826. *
  827. * @return bool If the operation succeeded
  828. */
  829. protected function downloadVersionData(&$data, &$error)
  830. {
  831. $url = $this->baseUrl.'/versions';
  832. $errFmt = 'The "%s" file could not be %s: %s';
  833. if (!$json = $this->httpClient->get($url)) {
  834. $error = sprintf($errFmt, $url, 'downloaded', $this->errHandler->message);
  835. return false;
  836. }
  837. if (!$data = json_decode($json, true)) {
  838. $error = sprintf($errFmt, $url, 'json-decoded', $this->getJsonError());
  839. return false;
  840. }
  841. return true;
  842. }
  843. /**
  844. * A wrapper around the methods needed to download and save the phar
  845. *
  846. * @param string $url The versioned download url
  847. * @param null|string $signature Set by method on successful download
  848. * @param null|string $error Set by method on failure
  849. *
  850. * @return bool If the operation succeeded
  851. */
  852. protected function downloadToTmp($url, &$signature, &$error)
  853. {
  854. $error = '';
  855. $errFmt = 'The "%s" file could not be downloaded: %s';
  856. $sigUrl = $url.'.sig';
  857. $this->errHandler->start();
  858. if (!$fh = fopen($this->tmpFile, 'w')) {
  859. $error = sprintf('Could not create file "%s": %s', $this->tmpFile, $this->errHandler->message);
  860. } elseif (!$this->getSignature($sigUrl, $signature)) {
  861. $error = sprintf($errFmt, $sigUrl, $this->errHandler->message);
  862. } elseif (!fwrite($fh, $this->httpClient->get($url))) {
  863. $error = sprintf($errFmt, $url, $this->errHandler->message);
  864. }
  865. if (is_resource($fh)) {
  866. fclose($fh);
  867. }
  868. $this->errHandler->stop();
  869. return empty($error);
  870. }
  871. /**
  872. * Verifies the downloaded file and saves it to the target location
  873. *
  874. * @param string $version The composer version downloaded
  875. * @param string $signature The digital signature to check
  876. * @param null|string $error Set by method on failure
  877. *
  878. * @return bool If the operation succeeded
  879. */
  880. protected function verifyAndSave($version, $signature, &$error)
  881. {
  882. $error = '';
  883. if (!$this->validatePhar($this->tmpFile, $pharError)) {
  884. $error = 'The download is corrupt: '.$pharError;
  885. } elseif (!$this->verifySignature($version, $signature, $this->tmpFile)) {
  886. $error = 'Signature mismatch, could not verify the phar file integrity';
  887. } else {
  888. $this->errHandler->start();
  889. if (!rename($this->tmpFile, $this->target)) {
  890. $error = sprintf('Could not write to file "%s": %s', $this->target, $this->errHandler->message);
  891. }
  892. chmod($this->target, 0755);
  893. $this->errHandler->stop();
  894. }
  895. return empty($error);
  896. }
  897. /**
  898. * Parses an array of version data to match the required channel
  899. *
  900. * @param array $data Downloaded version data
  901. * @param mixed $channel Version channel to use
  902. * @param false|string $version Set by method
  903. * @param mixed $url The versioned url, set by method
  904. */
  905. protected function parseVersionData(array $data, $channel, &$version, &$url)
  906. {
  907. foreach ($data[$channel] as $candidate) {
  908. if ($candidate['min-php'] <= PHP_VERSION_ID) {
  909. $version = $candidate['version'];
  910. $url = $this->baseUrl.$candidate['path'];
  911. break;
  912. }
  913. }
  914. if (!$version) {
  915. $error = sprintf(
  916. 'None of the %d %s version(s) of Composer matches your PHP version (%s / ID: %d)',
  917. count($data[$channel]),
  918. $channel,
  919. PHP_VERSION,
  920. PHP_VERSION_ID
  921. );
  922. throw new RuntimeException($error);
  923. }
  924. }
  925. /**
  926. * Downloads the digital signature of required phar file
  927. *
  928. * @param string $url The signature url
  929. * @param null|string $signature Set by method on success
  930. *
  931. * @return bool If the download succeeded
  932. */
  933. protected function getSignature($url, &$signature)
  934. {
  935. if (!$result = $this->disableTls) {
  936. $signature = $this->httpClient->get($url);
  937. if ($signature) {
  938. $signature = json_decode($signature, true);
  939. $signature = base64_decode($signature['sha384']);
  940. $result = true;
  941. }
  942. }
  943. return $result;
  944. }
  945. /**
  946. * Verifies the signature of the downloaded phar
  947. *
  948. * @param string $version The composer versione
  949. * @param string $signature The downloaded digital signature
  950. * @param string $file The temp phar file
  951. *
  952. * @return bool If the operation succeeded
  953. */
  954. protected function verifySignature($version, $signature, $file)
  955. {
  956. if (!$result = $this->disableTls) {
  957. $path = preg_match('{^[0-9a-f]{40}$}', $version) ? $this->pubKeys['dev'] : $this->pubKeys['tags'];
  958. $pubkeyid = openssl_pkey_get_public('file://'.$path);
  959. $result = 1 === openssl_verify(
  960. file_get_contents($file),
  961. $signature,
  962. $pubkeyid,
  963. $this->algo
  964. );
  965. // PHP 8 automatically frees the key instance and deprecates the function
  966. if (PHP_VERSION_ID < 80000) {
  967. openssl_free_key($pubkeyid);
  968. }
  969. }
  970. return $result;
  971. }
  972. /**
  973. * Validates the downloaded phar file
  974. *
  975. * @param string $pharFile The temp phar file
  976. * @param null|string $error Set by method on failure
  977. *
  978. * @return bool If the operation succeeded
  979. */
  980. protected function validatePhar($pharFile, &$error)
  981. {
  982. if (ini_get('phar.readonly')) {
  983. return true;
  984. }
  985. try {
  986. // Test the phar validity
  987. $phar = new Phar($pharFile);
  988. // Free the variable to unlock the file
  989. unset($phar);
  990. $result = true;
  991. } catch (Exception $e) {
  992. if (!$e instanceof UnexpectedValueException && !$e instanceof PharException) {
  993. throw $e;
  994. }
  995. $error = $e->getMessage();
  996. $result = false;
  997. }
  998. return $result;
  999. }
  1000. /**
  1001. * Returns a string representation of the last json error
  1002. *
  1003. * @return string The error string or code
  1004. */
  1005. protected function getJsonError()
  1006. {
  1007. if (function_exists('json_last_error_msg')) {
  1008. return json_last_error_msg();
  1009. } else {
  1010. return 'json_last_error = '.json_last_error();
  1011. }
  1012. }
  1013. /**
  1014. * Cleans up resources at the end of the installation
  1015. *
  1016. * @param bool $result If the installation succeeded
  1017. */
  1018. protected function cleanUp($result)
  1019. {
  1020. if ($this->quiet) {
  1021. // Ensure output buffers are emptied
  1022. $errors = explode(PHP_EOL, (string) ob_get_clean());
  1023. }
  1024. if (!$result) {
  1025. // Output buffered errors
  1026. if ($this->quiet) {
  1027. $this->outputErrors($errors);
  1028. }
  1029. // Clean up stuff we created
  1030. $this->uninstall();
  1031. } elseif ($this->tmpCafile !== null) {
  1032. @unlink($this->tmpCafile);
  1033. }
  1034. }
  1035. /**
  1036. * Outputs unique errors when in quiet mode
  1037. *
  1038. */
  1039. protected function outputErrors(array $errors)
  1040. {
  1041. $shown = array();
  1042. foreach ($errors as $error) {
  1043. if ($error && !in_array($error, $shown)) {
  1044. out($error, 'error');
  1045. $shown[] = $error;
  1046. }
  1047. }
  1048. }
  1049. /**
  1050. * Uninstalls newly-created files and directories on failure
  1051. *
  1052. */
  1053. protected function uninstall()
  1054. {
  1055. foreach (array_reverse($this->installs) as $target) {
  1056. if (is_file($target)) {
  1057. @unlink($target);
  1058. } elseif (is_dir($target)) {
  1059. @rmdir($target);
  1060. }
  1061. }
  1062. if ($this->tmpFile !== null && file_exists($this->tmpFile)) {
  1063. @unlink($this->tmpFile);
  1064. }
  1065. }
  1066. public static function getPKDev()
  1067. {
  1068. return <<<PKDEV
  1069. -----BEGIN PUBLIC KEY-----
  1070. MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnBDHjZS6e0ZMoK3xTD7f
  1071. FNCzlXjX/Aie2dit8QXA03pSrOTbaMnxON3hUL47Lz3g1SC6YJEMVHr0zYq4elWi
  1072. i3ecFEgzLcj+pZM5X6qWu2Ozz4vWx3JYo1/a/HYdOuW9e3lwS8VtS0AVJA+U8X0A
  1073. hZnBmGpltHhO8hPKHgkJtkTUxCheTcbqn4wGHl8Z2SediDcPTLwqezWKUfrYzu1f
  1074. o/j3WFwFs6GtK4wdYtiXr+yspBZHO3y1udf8eFFGcb2V3EaLOrtfur6XQVizjOuk
  1075. 8lw5zzse1Qp/klHqbDRsjSzJ6iL6F4aynBc6Euqt/8ccNAIz0rLjLhOraeyj4eNn
  1076. 8iokwMKiXpcrQLTKH+RH1JCuOVxQ436bJwbSsp1VwiqftPQieN+tzqy+EiHJJmGf
  1077. TBAbWcncicCk9q2md+AmhNbvHO4PWbbz9TzC7HJb460jyWeuMEvw3gNIpEo2jYa9
  1078. pMV6cVqnSa+wOc0D7pC9a6bne0bvLcm3S+w6I5iDB3lZsb3A9UtRiSP7aGSo7D72
  1079. 8tC8+cIgZcI7k9vjvOqH+d7sdOU2yPCnRY6wFh62/g8bDnUpr56nZN1G89GwM4d4
  1080. r/TU7BQQIzsZgAiqOGXvVklIgAMiV0iucgf3rNBLjjeNEwNSTTG9F0CtQ+7JLwaE
  1081. wSEuAuRm+pRqi8BRnQ/GKUcCAwEAAQ==
  1082. -----END PUBLIC KEY-----
  1083. PKDEV;
  1084. }
  1085. public static function getPKTags()
  1086. {
  1087. return <<<PKTAGS
  1088. -----BEGIN PUBLIC KEY-----
  1089. MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0Vi/2K6apCVj76nCnCl2
  1090. MQUPdK+A9eqkYBacXo2wQBYmyVlXm2/n/ZsX6pCLYPQTHyr5jXbkQzBw8SKqPdlh
  1091. vA7NpbMeNCz7wP/AobvUXM8xQuXKbMDTY2uZ4O7sM+PfGbptKPBGLe8Z8d2sUnTO
  1092. bXtX6Lrj13wkRto7st/w/Yp33RHe9SlqkiiS4MsH1jBkcIkEHsRaveZzedUaxY0M
  1093. mba0uPhGUInpPzEHwrYqBBEtWvP97t2vtfx8I5qv28kh0Y6t+jnjL1Urid2iuQZf
  1094. noCMFIOu4vksK5HxJxxrN0GOmGmwVQjOOtxkwikNiotZGPR4KsVj8NnBrLX7oGuM
  1095. nQvGciiu+KoC2r3HDBrpDeBVdOWxDzT5R4iI0KoLzFh2pKqwbY+obNPS2bj+2dgJ
  1096. rV3V5Jjry42QOCBN3c88wU1PKftOLj2ECpewY6vnE478IipiEu7EAdK8Zwj2LmTr
  1097. RKQUSa9k7ggBkYZWAeO/2Ag0ey3g2bg7eqk+sHEq5ynIXd5lhv6tC5PBdHlWipDK
  1098. tl2IxiEnejnOmAzGVivE1YGduYBjN+mjxDVy8KGBrjnz1JPgAvgdwJ2dYw4Rsc/e
  1099. TzCFWGk/HM6a4f0IzBWbJ5ot0PIi4amk07IotBXDWwqDiQTwyuGCym5EqWQ2BD95
  1100. RGv89BPD+2DLnJysngsvVaUCAwEAAQ==
  1101. -----END PUBLIC KEY-----
  1102. PKTAGS;
  1103. }
  1104. }
  1105. class ErrorHandler
  1106. {
  1107. public $message;
  1108. protected $active;
  1109. /**
  1110. * Handle php errors
  1111. *
  1112. * @param mixed $code The error code
  1113. * @param mixed $msg The error message
  1114. */
  1115. public function handleError($code, $msg)
  1116. {
  1117. if ($this->message) {
  1118. $this->message .= PHP_EOL;
  1119. }
  1120. $this->message .= preg_replace('{^file_get_contents\(.*?\): }', '', $msg);
  1121. }
  1122. /**
  1123. * Starts error-handling if not already active
  1124. *
  1125. * Any message is cleared
  1126. */
  1127. public function start()
  1128. {
  1129. if (!$this->active) {
  1130. set_error_handler(array($this, 'handleError'));
  1131. $this->active = true;
  1132. }
  1133. $this->message = '';
  1134. }
  1135. /**
  1136. * Stops error-handling if active
  1137. *
  1138. * Any message is preserved until the next call to start()
  1139. */
  1140. public function stop()
  1141. {
  1142. if ($this->active) {
  1143. restore_error_handler();
  1144. $this->active = false;
  1145. }
  1146. }
  1147. }
  1148. class NoProxyPattern
  1149. {
  1150. private $composerInNoProxy = false;
  1151. private $rulePorts = array();
  1152. public function __construct($pattern)
  1153. {
  1154. $rules = preg_split('{[\s,]+}', $pattern, null, PREG_SPLIT_NO_EMPTY);
  1155. if ($matches = preg_grep('{getcomposer\.org(?::\d+)?}i', $rules)) {
  1156. $this->composerInNoProxy = true;
  1157. foreach ($matches as $match) {
  1158. if (strpos($match, ':') !== false) {
  1159. list(, $port) = explode(':', $match);
  1160. $this->rulePorts[] = (int) $port;
  1161. }
  1162. }
  1163. }
  1164. }
  1165. /**
  1166. * Returns true if NO_PROXY contains getcomposer.org
  1167. *
  1168. * @param string $url http(s)://getcomposer.org
  1169. *
  1170. * @return bool
  1171. */
  1172. public function test($url)
  1173. {
  1174. if (!$this->composerInNoProxy) {
  1175. return false;
  1176. }
  1177. if (empty($this->rulePorts)) {
  1178. return true;
  1179. }
  1180. if (strpos($url, 'http://') === 0) {
  1181. $port = 80;
  1182. } else {
  1183. $port = 443;
  1184. }
  1185. return in_array($port, $this->rulePorts);
  1186. }
  1187. }
  1188. class HttpClient {
  1189. /** @var null|string */
  1190. private static $caPath;
  1191. private $options = array('http' => array());
  1192. private $disableTls = false;
  1193. public function __construct($disableTls = false, $cafile = false)
  1194. {
  1195. $this->disableTls = $disableTls;
  1196. if ($this->disableTls === false) {
  1197. if (!empty($cafile) && !is_dir($cafile)) {
  1198. if (!is_readable($cafile) || !validateCaFile(file_get_contents($cafile))) {
  1199. throw new RuntimeException('The configured cafile (' .$cafile. ') was not valid or could not be read.');
  1200. }
  1201. }
  1202. $options = $this->getTlsStreamContextDefaults($cafile);
  1203. $this->options = array_replace_recursive($this->options, $options);
  1204. }
  1205. }
  1206. public function get($url)
  1207. {
  1208. if (function_exists('http_clear_last_response_headers')) {
  1209. $http_response_header = http_clear_last_response_headers();
  1210. }
  1211. $context = $this->getStreamContext($url);
  1212. $result = file_get_contents($url, false, $context);
  1213. if ($result && extension_loaded('zlib')) {
  1214. if (function_exists('http_get_last_response_headers')) {
  1215. $http_response_header = http_get_last_response_headers();
  1216. }
  1217. $headers = $http_response_header;
  1218. $decode = false;
  1219. foreach ($headers as $header) {
  1220. if (preg_match('{^content-encoding: *gzip *$}i', $header)) {
  1221. $decode = true;
  1222. continue;
  1223. } elseif (preg_match('{^HTTP/}i', $header)) {
  1224. $decode = false;
  1225. }
  1226. }
  1227. if ($decode) {
  1228. if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
  1229. $result = zlib_decode($result);
  1230. } else {
  1231. // work around issue with gzuncompress & co that do not work with all gzip checksums
  1232. $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,'.base64_encode($result));
  1233. }
  1234. if (!$result) {
  1235. throw new RuntimeException('Failed to decode zlib stream');
  1236. }
  1237. }
  1238. }
  1239. return $result;
  1240. }
  1241. protected function getStreamContext($url)
  1242. {
  1243. if ($this->disableTls === false) {
  1244. if (PHP_VERSION_ID < 50600) {
  1245. $this->options['ssl']['SNI_server_name'] = parse_url($url, PHP_URL_HOST);
  1246. }
  1247. }
  1248. // Keeping the above mostly isolated from the code copied from Composer.
  1249. return $this->getMergedStreamContext($url);
  1250. }
  1251. protected function getTlsStreamContextDefaults($cafile)
  1252. {
  1253. $ciphers = implode(':', array(
  1254. 'ECDHE-RSA-AES128-GCM-SHA256',
  1255. 'ECDHE-ECDSA-AES128-GCM-SHA256',
  1256. 'ECDHE-RSA-AES256-GCM-SHA384',
  1257. 'ECDHE-ECDSA-AES256-GCM-SHA384',
  1258. 'DHE-RSA-AES128-GCM-SHA256',
  1259. 'DHE-DSS-AES128-GCM-SHA256',
  1260. 'kEDH+AESGCM',
  1261. 'ECDHE-RSA-AES128-SHA256',
  1262. 'ECDHE-ECDSA-AES128-SHA256',
  1263. 'ECDHE-RSA-AES128-SHA',
  1264. 'ECDHE-ECDSA-AES128-SHA',
  1265. 'ECDHE-RSA-AES256-SHA384',
  1266. 'ECDHE-ECDSA-AES256-SHA384',
  1267. 'ECDHE-RSA-AES256-SHA',
  1268. 'ECDHE-ECDSA-AES256-SHA',
  1269. 'DHE-RSA-AES128-SHA256',
  1270. 'DHE-RSA-AES128-SHA',
  1271. 'DHE-DSS-AES128-SHA256',
  1272. 'DHE-RSA-AES256-SHA256',
  1273. 'DHE-DSS-AES256-SHA',
  1274. 'DHE-RSA-AES256-SHA',
  1275. 'AES128-GCM-SHA256',
  1276. 'AES256-GCM-SHA384',
  1277. 'AES128-SHA256',
  1278. 'AES256-SHA256',
  1279. 'AES128-SHA',
  1280. 'AES256-SHA',
  1281. 'AES',
  1282. 'CAMELLIA',
  1283. 'DES-CBC3-SHA',
  1284. '!aNULL',
  1285. '!eNULL',
  1286. '!EXPORT',
  1287. '!DES',
  1288. '!RC4',
  1289. '!MD5',
  1290. '!PSK',
  1291. '!aECDH',
  1292. '!EDH-DSS-DES-CBC3-SHA',
  1293. '!EDH-RSA-DES-CBC3-SHA',
  1294. '!KRB5-DES-CBC3-SHA',
  1295. ));
  1296. /**
  1297. * CN_match and SNI_server_name are only known once a URL is passed.
  1298. * They will be set in the getOptionsForUrl() method which receives a URL.
  1299. *
  1300. * cafile or capath can be overridden by passing in those options to constructor.
  1301. */
  1302. $options = array(
  1303. 'ssl' => array(
  1304. 'ciphers' => $ciphers,
  1305. 'verify_peer' => true,
  1306. 'verify_depth' => 7,
  1307. 'SNI_enabled' => true,
  1308. )
  1309. );
  1310. /**
  1311. * Attempt to find a local cafile or throw an exception.
  1312. * The user may go download one if this occurs.
  1313. */
  1314. if (!$cafile) {
  1315. $cafile = self::getSystemCaRootBundlePath();
  1316. }
  1317. if (is_dir($cafile)) {
  1318. $options['ssl']['capath'] = $cafile;
  1319. } elseif ($cafile) {
  1320. $options['ssl']['cafile'] = $cafile;
  1321. } else {
  1322. throw new RuntimeException('A valid cafile could not be located automatically.');
  1323. }
  1324. /**
  1325. * Disable TLS compression to prevent CRIME attacks where supported.
  1326. */
  1327. if (version_compare(PHP_VERSION, '5.4.13') >= 0) {
  1328. $options['ssl']['disable_compression'] = true;
  1329. }
  1330. return $options;
  1331. }
  1332. /**
  1333. * function copied from Composer\Util\StreamContextFactory::initOptions
  1334. *
  1335. * Any changes should be applied there as well, or backported here.
  1336. *
  1337. * @param string $url URL the context is to be used for
  1338. * @return resource Default context
  1339. * @throws \RuntimeException if https proxy required and OpenSSL uninstalled
  1340. */
  1341. protected function getMergedStreamContext($url)
  1342. {
  1343. $options = $this->options;
  1344. // Handle HTTP_PROXY/http_proxy on CLI only for security reasons
  1345. if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && (!empty($_SERVER['HTTP_PROXY']) || !empty($_SERVER['http_proxy']))) {
  1346. $proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']);
  1347. }
  1348. // Prefer CGI_HTTP_PROXY if available
  1349. if (!empty($_SERVER['CGI_HTTP_PROXY'])) {
  1350. $proxy = parse_url($_SERVER['CGI_HTTP_PROXY']);
  1351. }
  1352. // Override with HTTPS proxy if present and URL is https
  1353. if (preg_match('{^https://}i', $url) && (!empty($_SERVER['HTTPS_PROXY']) || !empty($_SERVER['https_proxy']))) {
  1354. $proxy = parse_url(!empty($_SERVER['https_proxy']) ? $_SERVER['https_proxy'] : $_SERVER['HTTPS_PROXY']);
  1355. }
  1356. // Remove proxy if URL matches no_proxy directive
  1357. if (!empty($_SERVER['NO_PROXY']) || !empty($_SERVER['no_proxy']) && parse_url($url, PHP_URL_HOST)) {
  1358. $pattern = new NoProxyPattern(!empty($_SERVER['no_proxy']) ? $_SERVER['no_proxy'] : $_SERVER['NO_PROXY']);
  1359. if ($pattern->test($url)) {
  1360. unset($proxy);
  1361. }
  1362. }
  1363. if (!empty($proxy)) {
  1364. $proxyURL = isset($proxy['scheme']) ? $proxy['scheme'] . '://' : '';
  1365. $proxyURL .= isset($proxy['host']) ? $proxy['host'] : '';
  1366. if (isset($proxy['port'])) {
  1367. $proxyURL .= ":" . $proxy['port'];
  1368. } elseif (strpos($proxyURL, 'http://') === 0) {
  1369. $proxyURL .= ":80";
  1370. } elseif (strpos($proxyURL, 'https://') === 0) {
  1371. $proxyURL .= ":443";
  1372. }
  1373. // check for a secure proxy
  1374. if (strpos($proxyURL, 'https://') === 0) {
  1375. if (!extension_loaded('openssl')) {
  1376. throw new RuntimeException('You must enable the openssl extension to use a secure proxy.');
  1377. }
  1378. if (strpos($url, 'https://') === 0) {
  1379. throw new RuntimeException('PHP does not support https requests through a secure proxy.');
  1380. }
  1381. }
  1382. // http(s):// is not supported in proxy
  1383. $proxyURL = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyURL);
  1384. $options['http'] = array(
  1385. 'proxy' => $proxyURL,
  1386. );
  1387. // add request_fulluri for http requests
  1388. if ('http' === parse_url($url, PHP_URL_SCHEME)) {
  1389. $options['http']['request_fulluri'] = true;
  1390. }
  1391. // handle proxy auth if present
  1392. if (isset($proxy['user'])) {
  1393. $auth = rawurldecode($proxy['user']);
  1394. if (isset($proxy['pass'])) {
  1395. $auth .= ':' . rawurldecode($proxy['pass']);
  1396. }
  1397. $auth = base64_encode($auth);
  1398. $options['http']['header'] = "Proxy-Authorization: Basic {$auth}\r\n";
  1399. }
  1400. }
  1401. if (isset($options['http']['header'])) {
  1402. $options['http']['header'] .= "Connection: close\r\n";
  1403. } else {
  1404. $options['http']['header'] = "Connection: close\r\n";
  1405. }
  1406. if (extension_loaded('zlib')) {
  1407. $options['http']['header'] .= "Accept-Encoding: gzip\r\n";
  1408. }
  1409. $options['http']['header'] .= "User-Agent: ".COMPOSER_INSTALLER."\r\n";
  1410. $options['http']['protocol_version'] = 1.1;
  1411. $options['http']['timeout'] = 600;
  1412. return stream_context_create($options);
  1413. }
  1414. /**
  1415. * This method was adapted from Sslurp.
  1416. * https://github.com/EvanDotPro/Sslurp
  1417. *
  1418. * (c) Evan Coury <me@evancoury.com>
  1419. *
  1420. * For the full copyright and license information, please see below:
  1421. *
  1422. * Copyright (c) 2013, Evan Coury
  1423. * All rights reserved.
  1424. *
  1425. * Redistribution and use in source and binary forms, with or without modification,
  1426. * are permitted provided that the following conditions are met:
  1427. *
  1428. * * Redistributions of source code must retain the above copyright notice,
  1429. * this list of conditions and the following disclaimer.
  1430. *
  1431. * * Redistributions in binary form must reproduce the above copyright notice,
  1432. * this list of conditions and the following disclaimer in the documentation
  1433. * and/or other materials provided with the distribution.
  1434. *
  1435. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  1436. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  1437. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  1438. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  1439. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  1440. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  1441. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  1442. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  1443. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  1444. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  1445. */
  1446. public static function getSystemCaRootBundlePath()
  1447. {
  1448. if (self::$caPath !== null) {
  1449. return self::$caPath;
  1450. }
  1451. // If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that.
  1452. // This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
  1453. $envCertFile = getenv('SSL_CERT_FILE');
  1454. if ($envCertFile && is_readable($envCertFile) && validateCaFile(file_get_contents($envCertFile))) {
  1455. return self::$caPath = $envCertFile;
  1456. }
  1457. // If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that.
  1458. // This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
  1459. $envCertDir = getenv('SSL_CERT_DIR');
  1460. if ($envCertDir && is_dir($envCertDir) && is_readable($envCertDir)) {
  1461. return self::$caPath = $envCertDir;
  1462. }
  1463. $configured = ini_get('openssl.cafile');
  1464. if ($configured && strlen($configured) > 0 && is_readable($configured) && validateCaFile(file_get_contents($configured))) {
  1465. return self::$caPath = $configured;
  1466. }
  1467. $configured = ini_get('openssl.capath');
  1468. if ($configured && is_dir($configured) && is_readable($configured)) {
  1469. return self::$caPath = $configured;
  1470. }
  1471. $caBundlePaths = array(
  1472. '/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package)
  1473. '/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package)
  1474. '/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package)
  1475. '/usr/local/share/certs/ca-root-nss.crt', // FreeBSD (ca_root_nss_package)
  1476. '/usr/ssl/certs/ca-bundle.crt', // Cygwin
  1477. '/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package
  1478. '/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option)
  1479. '/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat?
  1480. '/etc/ssl/cert.pem', // OpenBSD
  1481. '/usr/local/etc/ssl/cert.pem', // FreeBSD 10.x
  1482. '/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package
  1483. '/usr/local/etc/openssl@1.1/cert.pem', // OS X homebrew, openssl@1.1 package
  1484. '/opt/homebrew/etc/openssl@3/cert.pem', // macOS silicon homebrew, openssl@3 package
  1485. '/opt/homebrew/etc/openssl@1.1/cert.pem', // macOS silicon homebrew, openssl@1.1 package
  1486. );
  1487. foreach ($caBundlePaths as $caBundle) {
  1488. if (@is_readable($caBundle) && validateCaFile(file_get_contents($caBundle))) {
  1489. return self::$caPath = $caBundle;
  1490. }
  1491. }
  1492. foreach ($caBundlePaths as $caBundle) {
  1493. $caBundle = dirname($caBundle);
  1494. if (is_dir($caBundle) && glob($caBundle.'/*')) {
  1495. return self::$caPath = $caBundle;
  1496. }
  1497. }
  1498. return self::$caPath = false;
  1499. }
  1500. public static function getPackagedCaFile()
  1501. {
  1502. return <<<CACERT
  1503. ##
  1504. ## Bundle of CA Root Certificates for Let's Encrypt
  1505. ##
  1506. ## See https://letsencrypt.org/certificates/#root-certificates
  1507. ##
  1508. ## ISRG Root X1 (RSA 4096) expires Jun 04 11:04:38 2035 GMT
  1509. ## ISRG Root X2 (ECDSA P-384) expires Sep 17 16:00:00 2040 GMT
  1510. ##
  1511. ## Both these are self-signed CA root certificates
  1512. ##
  1513. ISRG Root X1
  1514. ============
  1515. -----BEGIN CERTIFICATE-----
  1516. MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
  1517. TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
  1518. cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
  1519. WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
  1520. ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
  1521. MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
  1522. h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
  1523. 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
  1524. A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
  1525. T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
  1526. B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
  1527. B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
  1528. KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
  1529. OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
  1530. jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
  1531. qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
  1532. rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
  1533. HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
  1534. hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
  1535. ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
  1536. 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
  1537. NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
  1538. ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
  1539. TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
  1540. jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
  1541. oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
  1542. 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
  1543. mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
  1544. emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
  1545. -----END CERTIFICATE-----
  1546. ISRG Root X2
  1547. ============
  1548. -----BEGIN CERTIFICATE-----
  1549. MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
  1550. CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
  1551. R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
  1552. MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
  1553. ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
  1554. EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
  1555. +1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
  1556. ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
  1557. AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
  1558. zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
  1559. tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
  1560. /q4AaOeMSQ+2b1tbFfLn
  1561. -----END CERTIFICATE-----
  1562. CACERT;
  1563. }
  1564. }