PageRenderTime 51ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/saf/lib/PEAR/PEAR/Common.php

https://github.com/cbrunet/sitellite
PHP | 1552 lines | 1163 code | 68 blank | 321 comment | 102 complexity | 6fab59496c247cefd9956ab34d7a9dea MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0, GPL-3.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Stig Bakken <ssb@fast.no> |
  17. // | Tomas V.V.Cox <cox@idecnet.com> |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: Common.php,v 1.1.1.1 2005/04/29 04:44:36 lux Exp $
  21. require_once 'PEAR.php';
  22. require_once 'Archive/Tar.php';
  23. require_once 'System.php';
  24. require_once 'PEAR/Config.php';
  25. // {{{ constants and globals
  26. define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^([A-Z][a-zA-Z0-9_]+|[a-z][a-z0-9_]+)$/');
  27. /**
  28. * List of temporary files and directories registered by
  29. * PEAR_Common::addTempFile().
  30. * @var array
  31. */
  32. $GLOBALS['_PEAR_Common_tempfiles'] = array();
  33. /**
  34. * Valid maintainer roles
  35. * @var array
  36. */
  37. $GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
  38. /**
  39. * Valid release states
  40. * @var array
  41. */
  42. $GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
  43. /**
  44. * Valid dependency types
  45. * @var array
  46. */
  47. $GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
  48. /**
  49. * Valid dependency relations
  50. * @var array
  51. */
  52. $GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge');
  53. /**
  54. * Valid file roles
  55. * @var array
  56. */
  57. $GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
  58. /**
  59. * Valid replacement types
  60. * @var array
  61. */
  62. $GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
  63. /**
  64. * Valid "provide" types
  65. * @var array
  66. */
  67. $GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
  68. /**
  69. * Valid "provide" types
  70. * @var array
  71. */
  72. $GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');
  73. // }}}
  74. /**
  75. * Class providing common functionality for PEAR adminsitration classes.
  76. */
  77. class PEAR_Common extends PEAR
  78. {
  79. // {{{ properties
  80. /** stack of elements, gives some sort of XML context */
  81. var $element_stack = array();
  82. /** name of currently parsed XML element */
  83. var $current_element;
  84. /** array of attributes of the currently parsed XML element */
  85. var $current_attributes = array();
  86. /** assoc with information about a package */
  87. var $pkginfo = array();
  88. /**
  89. * User Interface object (PEAR_Frontend_* class). If null,
  90. * the log() method uses print.
  91. * @var object
  92. */
  93. var $ui = null;
  94. /**
  95. * Configuration object (PEAR_Config).
  96. * @var object
  97. */
  98. var $config = null;
  99. var $current_path = null;
  100. /**
  101. * PEAR_SourceAnalyzer instance
  102. * @var object
  103. */
  104. var $source_analyzer = null;
  105. // }}}
  106. // {{{ constructor
  107. /**
  108. * PEAR_Common constructor
  109. *
  110. * @access public
  111. */
  112. function PEAR_Common()
  113. {
  114. parent::PEAR();
  115. $this->config = &PEAR_Config::singleton();
  116. $this->debug = $this->config->get('verbose');
  117. }
  118. // }}}
  119. // {{{ destructor
  120. /**
  121. * PEAR_Common destructor
  122. *
  123. * @access private
  124. */
  125. function _PEAR_Common()
  126. {
  127. // doesn't work due to bug #14744
  128. //$tempfiles = $this->_tempfiles;
  129. $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
  130. while ($file = array_shift($tempfiles)) {
  131. if (@is_dir($file)) {
  132. System::rm("-rf $file");
  133. } elseif (file_exists($file)) {
  134. unlink($file);
  135. }
  136. }
  137. }
  138. // }}}
  139. // {{{ addTempFile()
  140. /**
  141. * Register a temporary file or directory. When the destructor is
  142. * executed, all registered temporary files and directories are
  143. * removed.
  144. *
  145. * @param string $file name of file or directory
  146. *
  147. * @return void
  148. *
  149. * @access public
  150. */
  151. function addTempFile($file)
  152. {
  153. $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
  154. }
  155. // }}}
  156. // {{{ mkDirHier()
  157. /**
  158. * Wrapper to System::mkDir(), creates a directory as well as
  159. * any necessary parent directories.
  160. *
  161. * @param string $dir directory name
  162. *
  163. * @return bool TRUE on success, or a PEAR error
  164. *
  165. * @access public
  166. */
  167. function mkDirHier($dir)
  168. {
  169. $this->log(2, "+ create dir $dir");
  170. return System::mkDir("-p $dir");
  171. }
  172. // }}}
  173. // {{{ log()
  174. /**
  175. * Logging method.
  176. *
  177. * @param int $level log level (0 is quiet, higher is noisier)
  178. * @param string $msg message to write to the log
  179. *
  180. * @return void
  181. *
  182. * @access public
  183. */
  184. function log($level, $msg)
  185. {
  186. if ($this->debug >= $level) {
  187. if (is_object($this->ui)) {
  188. $this->ui->log($msg);
  189. } else {
  190. print "$msg\n";
  191. }
  192. }
  193. }
  194. // }}}
  195. // {{{ mkTempDir()
  196. /**
  197. * Create and register a temporary directory.
  198. *
  199. * @param string $tmpdir (optional) Directory to use as tmpdir.
  200. * Will use system defaults (for example
  201. * /tmp or c:\windows\temp) if not specified
  202. *
  203. * @return string name of created directory
  204. *
  205. * @access public
  206. */
  207. function mkTempDir($tmpdir = '')
  208. {
  209. if ($tmpdir) {
  210. $topt = "-t $tmpdir ";
  211. } else {
  212. $topt = '';
  213. }
  214. if (!$tmpdir = System::mktemp($topt . '-d pear')) {
  215. return false;
  216. }
  217. $this->addTempFile($tmpdir);
  218. return $tmpdir;
  219. }
  220. // }}}
  221. // {{{ setFrontendObject()
  222. /**
  223. * Set object that represents the frontend to be used.
  224. *
  225. * @param object Reference of the frontend object
  226. * @return void
  227. * @access public
  228. */
  229. function setFrontendObject(&$ui)
  230. {
  231. $this->ui = &$ui;
  232. }
  233. // }}}
  234. // {{{ _unIndent()
  235. /**
  236. * Unindent given string (?)
  237. *
  238. * @param string $str The string that has to be unindented.
  239. * @return string
  240. * @access private
  241. */
  242. function _unIndent($str)
  243. {
  244. // remove leading newlines
  245. $str = preg_replace('/^[\r\n]+/', '', $str);
  246. // find whitespace at the beginning of the first line
  247. $indent_len = strspn($str, " \t");
  248. $indent = substr($str, 0, $indent_len);
  249. $data = '';
  250. // remove the same amount of whitespace from following lines
  251. foreach (explode("\n", $str) as $line) {
  252. if (substr($line, 0, $indent_len) == $indent) {
  253. $data .= substr($line, $indent_len) . "\n";
  254. }
  255. }
  256. return $data;
  257. }
  258. // }}}
  259. // {{{ _element_start()
  260. /**
  261. * XML parser callback for starting elements. Used while package
  262. * format version is not yet known.
  263. *
  264. * @param resource $xp XML parser resource
  265. * @param string $name name of starting element
  266. * @param array $attribs element attributes, name => value
  267. *
  268. * @return void
  269. *
  270. * @access private
  271. */
  272. function _element_start($xp, $name, $attribs)
  273. {
  274. array_push($this->element_stack, $name);
  275. $this->current_element = $name;
  276. $spos = sizeof($this->element_stack) - 2;
  277. $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
  278. $this->current_attributes = $attribs;
  279. switch ($name) {
  280. case 'package': {
  281. if (isset($attribs['version'])) {
  282. $vs = preg_replace('/[^0-9a-z]/', '_', $attribs['version']);
  283. } else {
  284. $vs = '1_0';
  285. }
  286. $elem_start = '_element_start_'. $vs;
  287. $elem_end = '_element_end_'. $vs;
  288. $cdata = '_pkginfo_cdata_'. $vs;
  289. xml_set_element_handler($xp, $elem_start, $elem_end);
  290. xml_set_character_data_handler($xp, $cdata);
  291. break;
  292. }
  293. }
  294. }
  295. // }}}
  296. // {{{ _element_end()
  297. /**
  298. * XML parser callback for ending elements. Used while package
  299. * format version is not yet known.
  300. *
  301. * @param resource $xp XML parser resource
  302. * @param string $name name of ending element
  303. *
  304. * @return void
  305. *
  306. * @access private
  307. */
  308. function _element_end($xp, $name)
  309. {
  310. }
  311. // }}}
  312. // Support for package DTD v1.0:
  313. // {{{ _element_start_1_0()
  314. /**
  315. * XML parser callback for ending elements. Used for version 1.0
  316. * packages.
  317. *
  318. * @param resource $xp XML parser resource
  319. * @param string $name name of ending element
  320. *
  321. * @return void
  322. *
  323. * @access private
  324. */
  325. function _element_start_1_0($xp, $name, $attribs)
  326. {
  327. array_push($this->element_stack, $name);
  328. $this->current_element = $name;
  329. $spos = sizeof($this->element_stack) - 2;
  330. $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
  331. $this->current_attributes = $attribs;
  332. $this->cdata = '';
  333. switch ($name) {
  334. case 'dir':
  335. if ($this->in_changelog) {
  336. break;
  337. }
  338. if ($attribs['name'] != '/') {
  339. $this->dir_names[] = $attribs['name'];
  340. }
  341. if (isset($attribs['baseinstalldir'])) {
  342. $this->dir_install = $attribs['baseinstalldir'];
  343. }
  344. if (isset($attribs['role'])) {
  345. $this->dir_role = $attribs['role'];
  346. }
  347. break;
  348. case 'file':
  349. if ($this->in_changelog) {
  350. break;
  351. }
  352. if (isset($attribs['name'])) {
  353. $path = '';
  354. if (count($this->dir_names)) {
  355. foreach ($this->dir_names as $dir) {
  356. $path .= $dir . DIRECTORY_SEPARATOR;
  357. }
  358. }
  359. $path .= $attribs['name'];
  360. unset($attribs['name']);
  361. $this->current_path = $path;
  362. $this->filelist[$path] = $attribs;
  363. // Set the baseinstalldir only if the file don't have this attrib
  364. if (!isset($this->filelist[$path]['baseinstalldir']) &&
  365. isset($this->dir_install))
  366. {
  367. $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
  368. }
  369. // Set the Role
  370. if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
  371. $this->filelist[$path]['role'] = $this->dir_role;
  372. }
  373. }
  374. break;
  375. case 'replace':
  376. if (!$this->in_changelog) {
  377. $this->filelist[$this->current_path]['replacements'][] = $attribs;
  378. }
  379. break;
  380. case 'maintainers':
  381. $this->pkginfo['maintainers'] = array();
  382. $this->m_i = 0; // maintainers array index
  383. break;
  384. case 'maintainer':
  385. // compatibility check
  386. if (!isset($this->pkginfo['maintainers'])) {
  387. $this->pkginfo['maintainers'] = array();
  388. $this->m_i = 0;
  389. }
  390. $this->pkginfo['maintainers'][$this->m_i] = array();
  391. $this->current_maintainer =& $this->pkginfo['maintainers'][$this->m_i];
  392. break;
  393. case 'changelog':
  394. $this->pkginfo['changelog'] = array();
  395. $this->c_i = 0; // changelog array index
  396. $this->in_changelog = true;
  397. break;
  398. case 'release':
  399. if ($this->in_changelog) {
  400. $this->pkginfo['changelog'][$this->c_i] = array();
  401. $this->current_release = &$this->pkginfo['changelog'][$this->c_i];
  402. } else {
  403. $this->current_release = &$this->pkginfo;
  404. }
  405. break;
  406. case 'deps':
  407. if (!$this->in_changelog) {
  408. $this->pkginfo['release_deps'] = array();
  409. }
  410. break;
  411. case 'dep':
  412. // dependencies array index
  413. if (!$this->in_changelog) {
  414. $this->d_i++;
  415. $this->pkginfo['release_deps'][$this->d_i] = $attribs;
  416. }
  417. break;
  418. case 'configureoptions':
  419. if (!$this->in_changelog) {
  420. $this->pkginfo['configure_options'] = array();
  421. }
  422. break;
  423. case 'configureoption':
  424. if (!$this->in_changelog) {
  425. $this->pkginfo['configure_options'][] = $attribs;
  426. }
  427. break;
  428. }
  429. }
  430. // }}}
  431. // {{{ _element_end_1_0()
  432. /**
  433. * XML parser callback for ending elements. Used for version 1.0
  434. * packages.
  435. *
  436. * @param resource $xp XML parser resource
  437. * @param string $name name of ending element
  438. *
  439. * @return void
  440. *
  441. * @access private
  442. */
  443. function _element_end_1_0($xp, $name)
  444. {
  445. $data = trim($this->cdata);
  446. switch ($name) {
  447. case 'name':
  448. switch ($this->prev_element) {
  449. case 'package':
  450. // XXX should we check the package name here?
  451. $this->pkginfo['package'] = ereg_replace('[^a-zA-Z0-9._]', '_', $data);
  452. break;
  453. case 'maintainer':
  454. $this->current_maintainer['name'] = $data;
  455. break;
  456. }
  457. break;
  458. case 'summary':
  459. $this->pkginfo['summary'] = $data;
  460. break;
  461. case 'description':
  462. $data = $this->_unIndent($this->cdata);
  463. $this->pkginfo['description'] = $data;
  464. break;
  465. case 'user':
  466. $this->current_maintainer['handle'] = $data;
  467. break;
  468. case 'email':
  469. $this->current_maintainer['email'] = $data;
  470. break;
  471. case 'role':
  472. $this->current_maintainer['role'] = $data;
  473. break;
  474. case 'version':
  475. $data = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $data);
  476. if ($this->in_changelog) {
  477. $this->current_release['version'] = $data;
  478. } else {
  479. $this->pkginfo['version'] = $data;
  480. }
  481. break;
  482. case 'date':
  483. if ($this->in_changelog) {
  484. $this->current_release['release_date'] = $data;
  485. } else {
  486. $this->pkginfo['release_date'] = $data;
  487. }
  488. break;
  489. case 'notes':
  490. // try to "de-indent" release notes in case someone
  491. // has been over-indenting their xml ;-)
  492. $data = $this->_unIndent($this->cdata);
  493. if ($this->in_changelog) {
  494. $this->current_release['release_notes'] = $data;
  495. } else {
  496. $this->pkginfo['release_notes'] = $data;
  497. }
  498. break;
  499. case 'warnings':
  500. if ($this->in_changelog) {
  501. $this->current_release['release_warnings'] = $data;
  502. } else {
  503. $this->pkginfo['release_warnings'] = $data;
  504. }
  505. break;
  506. case 'state':
  507. if ($this->in_changelog) {
  508. $this->current_release['release_state'] = $data;
  509. } else {
  510. $this->pkginfo['release_state'] = $data;
  511. }
  512. break;
  513. case 'license':
  514. $this->pkginfo['release_license'] = $data;
  515. break;
  516. case 'dep':
  517. if ($data && !$this->in_changelog) {
  518. $this->pkginfo['release_deps'][$this->d_i]['name'] = $data;
  519. }
  520. break;
  521. case 'dir':
  522. if ($this->in_changelog) {
  523. break;
  524. }
  525. array_pop($this->dir_names);
  526. break;
  527. case 'file':
  528. if ($this->in_changelog) {
  529. break;
  530. }
  531. if ($data) {
  532. $path = '';
  533. if (count($this->dir_names)) {
  534. foreach ($this->dir_names as $dir) {
  535. $path .= $dir . DIRECTORY_SEPARATOR;
  536. }
  537. }
  538. $path .= $data;
  539. $this->filelist[$path] = $this->current_attributes;
  540. // Set the baseinstalldir only if the file don't have this attrib
  541. if (!isset($this->filelist[$path]['baseinstalldir']) &&
  542. isset($this->dir_install))
  543. {
  544. $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
  545. }
  546. // Set the Role
  547. if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
  548. $this->filelist[$path]['role'] = $this->dir_role;
  549. }
  550. }
  551. break;
  552. case 'maintainer':
  553. if (empty($this->pkginfo['maintainers'][$this->m_i]['role'])) {
  554. $this->pkginfo['maintainers'][$this->m_i]['role'] = 'lead';
  555. }
  556. $this->m_i++;
  557. break;
  558. case 'release':
  559. if ($this->in_changelog) {
  560. $this->c_i++;
  561. }
  562. break;
  563. case 'changelog':
  564. $this->in_changelog = false;
  565. break;
  566. case 'summary':
  567. $this->pkginfo['summary'] = $data;
  568. break;
  569. }
  570. array_pop($this->element_stack);
  571. $spos = sizeof($this->element_stack) - 1;
  572. $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
  573. $this->cdata = '';
  574. }
  575. // }}}
  576. // {{{ _pkginfo_cdata_1_0()
  577. /**
  578. * XML parser callback for character data. Used for version 1.0
  579. * packages.
  580. *
  581. * @param resource $xp XML parser resource
  582. * @param string $name character data
  583. *
  584. * @return void
  585. *
  586. * @access private
  587. */
  588. function _pkginfo_cdata_1_0($xp, $data)
  589. {
  590. if (isset($this->cdata)) {
  591. $this->cdata .= $data;
  592. }
  593. }
  594. // }}}
  595. // {{{ infoFromTgzFile()
  596. /**
  597. * Returns information about a package file. Expects the name of
  598. * a gzipped tar file as input.
  599. *
  600. * @param string $file name of .tgz file
  601. *
  602. * @return array array with package information
  603. *
  604. * @access public
  605. *
  606. */
  607. function infoFromTgzFile($file)
  608. {
  609. if (!@is_file($file)) {
  610. return $this->raiseError("could not open file \"$file\"");
  611. }
  612. $tar = new Archive_Tar($file);
  613. $content = $tar->listContent();
  614. if (!is_array($content)) {
  615. return $this->raiseError("could not get contents of package \"$file\"");
  616. }
  617. $xml = null;
  618. foreach ($content as $file) {
  619. $name = $file['filename'];
  620. if ($name == 'package.xml') {
  621. $xml = $name;
  622. break;
  623. } elseif (ereg('package.xml$', $name, $match)) {
  624. $xml = $match[0];
  625. break;
  626. }
  627. }
  628. $tmpdir = System::mkTemp('-d pear');
  629. $this->addTempFile($tmpdir);
  630. if (!$xml || !$tar->extractList($xml, $tmpdir)) {
  631. return $this->raiseError('could not extract the package.xml file');
  632. }
  633. return $this->infoFromDescriptionFile("$tmpdir/$xml");
  634. }
  635. // }}}
  636. // {{{ infoFromDescriptionFile()
  637. /**
  638. * Returns information about a package file. Expects the name of
  639. * a package xml file as input.
  640. *
  641. * @param string $descfile name of package xml file
  642. *
  643. * @return array array with package information
  644. *
  645. * @access public
  646. *
  647. */
  648. function infoFromDescriptionFile($descfile)
  649. {
  650. if (!@is_file($descfile) || !is_readable($descfile) ||
  651. (!$fp = @fopen($descfile, 'r'))) {
  652. return $this->raiseError("Unable to open $descfile");
  653. }
  654. // read the whole thing so we only get one cdata callback
  655. // for each block of cdata
  656. $data = fread($fp, filesize($descfile));
  657. return $this->infoFromString($data);
  658. }
  659. // }}}
  660. // {{{ infoFromString()
  661. /**
  662. * Returns information about a package file. Expects the contents
  663. * of a package xml file as input.
  664. *
  665. * @param string $data name of package xml file
  666. *
  667. * @return array array with package information
  668. *
  669. * @access public
  670. *
  671. */
  672. function infoFromString($data)
  673. {
  674. require_once('PEAR/Dependency.php');
  675. if (PEAR_Dependency::checkExtension($error, 'xml')) {
  676. return $this->raiseError($error);
  677. }
  678. $xp = @xml_parser_create();
  679. if (!$xp) {
  680. return $this->raiseError('Unable to create XML parser');
  681. }
  682. xml_set_object($xp, $this);
  683. xml_set_element_handler($xp, '_element_start', '_element_end');
  684. xml_set_character_data_handler($xp, '_pkginfo_cdata');
  685. xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
  686. $this->element_stack = array();
  687. $this->pkginfo = array();
  688. $this->current_element = false;
  689. unset($this->dir_install);
  690. $this->pkginfo['filelist'] = array();
  691. $this->filelist =& $this->pkginfo['filelist'];
  692. $this->dir_names = array();
  693. $this->in_changelog = false;
  694. $this->d_i = 0;
  695. $this->cdata = '';
  696. if (!xml_parse($xp, $data, 1)) {
  697. $code = xml_get_error_code($xp);
  698. $msg = sprintf("XML error: %s at line %d",
  699. xml_error_string($code),
  700. xml_get_current_line_number($xp));
  701. xml_parser_free($xp);
  702. return $this->raiseError($msg, $code);
  703. }
  704. xml_parser_free($xp);
  705. foreach ($this->pkginfo as $k => $v) {
  706. if (!is_array($v)) {
  707. $this->pkginfo[$k] = trim($v);
  708. }
  709. }
  710. return $this->pkginfo;
  711. }
  712. // }}}
  713. // {{{ infoFromAny()
  714. /**
  715. * Returns package information from different sources
  716. *
  717. * This method is able to extract information about a package
  718. * from a .tgz archive or from a XML package definition file.
  719. *
  720. * @access public
  721. * @param string Filename of the source ('package.xml', '<package>.tgz')
  722. * @return string
  723. */
  724. function infoFromAny($info)
  725. {
  726. if (is_string($info) && file_exists($info)) {
  727. $tmp = substr($info, -4);
  728. if ($tmp == '.xml') {
  729. $info = $this->infoFromDescriptionFile($info);
  730. } elseif ($tmp == '.tar' || $tmp == '.tgz') {
  731. $info = $this->infoFromTgzFile($info);
  732. } else {
  733. $fp = fopen($info, "r");
  734. $test = fread($fp, 5);
  735. fclose($fp);
  736. if ($test == "<?xml") {
  737. $info = $this->infoFromDescriptionFile($info);
  738. } else {
  739. $info = $this->infoFromTgzFile($info);
  740. }
  741. }
  742. if (PEAR::isError($info)) {
  743. return $this->raiseError($info);
  744. }
  745. }
  746. return $info;
  747. }
  748. // }}}
  749. // {{{ xmlFromInfo()
  750. /**
  751. * Return an XML document based on the package info (as returned
  752. * by the PEAR_Common::infoFrom* methods).
  753. *
  754. * @param array $pkginfo package info
  755. *
  756. * @return string XML data
  757. *
  758. * @access public
  759. */
  760. function xmlFromInfo($pkginfo)
  761. {
  762. static $maint_map = array(
  763. "handle" => "user",
  764. "name" => "name",
  765. "email" => "email",
  766. "role" => "role",
  767. );
  768. $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
  769. //$ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/package10.dtd\">\n";
  770. $ret .= "<package version=\"1.0\">
  771. <name>$pkginfo[package]</name>
  772. <summary>".htmlspecialchars($pkginfo['summary'])."</summary>
  773. <description>".htmlspecialchars($pkginfo['description'])."</description>
  774. <maintainers>
  775. ";
  776. foreach ($pkginfo['maintainers'] as $maint) {
  777. $ret .= " <maintainer>\n";
  778. foreach ($maint_map as $idx => $elm) {
  779. $ret .= " <$elm>";
  780. $ret .= htmlspecialchars($maint[$idx]);
  781. $ret .= "</$elm>\n";
  782. }
  783. $ret .= " </maintainer>\n";
  784. }
  785. $ret .= " </maintainers>\n";
  786. $ret .= $this->_makeReleaseXml($pkginfo);
  787. if (@sizeof($pkginfo['changelog']) > 0) {
  788. $ret .= " <changelog>\n";
  789. foreach ($pkginfo['changelog'] as $oldrelease) {
  790. $ret .= $this->_makeReleaseXml($oldrelease, true);
  791. }
  792. $ret .= " </changelog>\n";
  793. }
  794. $ret .= "</package>\n";
  795. return $ret;
  796. }
  797. // }}}
  798. // {{{ _makeReleaseXml()
  799. /**
  800. * Generate part of an XML description with release information.
  801. *
  802. * @param array $pkginfo array with release information
  803. * @param bool $changelog whether the result will be in a changelog element
  804. *
  805. * @return string XML data
  806. *
  807. * @access private
  808. */
  809. function _makeReleaseXml($pkginfo, $changelog = false)
  810. {
  811. // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
  812. $indent = $changelog ? " " : "";
  813. $ret = "$indent <release>\n";
  814. if (!empty($pkginfo['version'])) {
  815. $ret .= "$indent <version>$pkginfo[version]</version>\n";
  816. }
  817. if (!empty($pkginfo['release_date'])) {
  818. $ret .= "$indent <date>$pkginfo[release_date]</date>\n";
  819. }
  820. if (!empty($pkginfo['release_license'])) {
  821. $ret .= "$indent <license>$pkginfo[release_license]</license>\n";
  822. }
  823. if (!empty($pkginfo['release_state'])) {
  824. $ret .= "$indent <state>$pkginfo[release_state]</state>\n";
  825. }
  826. if (!empty($pkginfo['release_notes'])) {
  827. $ret .= "$indent <notes>".htmlspecialchars($pkginfo['release_notes'])."</notes>\n";
  828. }
  829. if (!empty($pkginfo['release_warnings'])) {
  830. $ret .= "$indent <warnings>".htmlspecialchars($pkginfo['release_warnings'])."</warnings>\n";
  831. }
  832. if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
  833. $ret .= "$indent <deps>\n";
  834. foreach ($pkginfo['release_deps'] as $dep) {
  835. $ret .= "$indent <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
  836. if (isset($dep['version'])) {
  837. $ret .= " version=\"$dep[version]\"";
  838. }
  839. if (isset($dep['name'])) {
  840. $ret .= ">$dep[name]</dep>\n";
  841. } else {
  842. $ret .= "/>\n";
  843. }
  844. }
  845. $ret .= "$indent </deps>\n";
  846. }
  847. if (isset($pkginfo['configure_options'])) {
  848. $ret .= "$indent <configureoptions>\n";
  849. foreach ($pkginfo['configure_options'] as $c) {
  850. $ret .= "$indent <configureoption name=\"".
  851. htmlspecialchars($c['name']) . "\"";
  852. if (isset($c['default'])) {
  853. $ret .= " default=\"" . htmlspecialchars($c['default']) . "\"";
  854. }
  855. $ret .= " prompt=\"" . htmlspecialchars($c['prompt']) . "\"";
  856. $ret .= "/>\n";
  857. }
  858. $ret .= "$indent </configureoptions>\n";
  859. }
  860. if (isset($pkginfo['filelist'])) {
  861. $ret .= "$indent <filelist>\n";
  862. foreach ($pkginfo['filelist'] as $file => $fa) {
  863. @$ret .= "$indent <file role=\"$fa[role]\"";
  864. if (isset($fa['baseinstalldir'])) {
  865. $ret .= ' baseinstalldir="' .
  866. htmlspecialchars($fa['baseinstalldir']) . '"';
  867. }
  868. if (isset($fa['md5sum'])) {
  869. $ret .= " md5sum=\"$fa[md5sum]\"";
  870. }
  871. if (isset($fa['platform'])) {
  872. $ret .= " platform=\"$fa[platform]\"";
  873. }
  874. if (!empty($fa['install-as'])) {
  875. $ret .= ' install-as="' .
  876. htmlspecialchars($fa['install-as']) . '"';
  877. }
  878. $ret .= ' name="' . htmlspecialchars($file) . '"';
  879. if (empty($fa['replacements'])) {
  880. $ret .= "/>\n";
  881. } else {
  882. $ret .= ">\n";
  883. foreach ($fa['replacements'] as $r) {
  884. $ret .= "$indent <replace";
  885. foreach ($r as $k => $v) {
  886. $ret .= " $k=\"" . htmlspecialchars($v) .'"';
  887. }
  888. $ret .= "/>\n";
  889. }
  890. @$ret .= "$indent </file>\n";
  891. }
  892. }
  893. $ret .= "$indent </filelist>\n";
  894. }
  895. $ret .= "$indent </release>\n";
  896. return $ret;
  897. }
  898. // }}}
  899. // {{{ validatePackageInfo()
  900. /**
  901. * Validate XML package definition file.
  902. *
  903. * @param string Filename of the package archive or of the package definition file
  904. * @param array Array that will contain the errors
  905. * @param array Array that will contain the warnings
  906. * @access public
  907. * @return boolean
  908. */
  909. function validatePackageInfo($info, &$errors, &$warnings)
  910. {
  911. global $_PEAR_Common_maintainer_roles,
  912. $_PEAR_Common_release_states,
  913. $_PEAR_Common_dependency_types,
  914. $_PEAR_Common_dependency_relations,
  915. $_PEAR_Common_file_roles,
  916. $_PEAR_Common_replacement_types;
  917. if (PEAR::isError($info = $this->infoFromAny($info))) {
  918. return $this->raiseError($info);
  919. }
  920. if (!is_array($info)) {
  921. return false;
  922. }
  923. $errors = array();
  924. $warnings = array();
  925. if (empty($info['package'])) {
  926. $errors[] = 'missing package name';
  927. }
  928. if (empty($info['summary'])) {
  929. $errors[] = 'missing summary';
  930. } elseif (strpos(trim($info['summary']), "\n") !== false) {
  931. $warnings[] = 'summary should be on a single line';
  932. }
  933. if (empty($info['description'])) {
  934. $errors[] = 'missing description';
  935. }
  936. if (empty($info['release_license'])) {
  937. $errors[] = 'missing license';
  938. }
  939. if (empty($info['version'])) {
  940. $errors[] = 'missing version';
  941. }
  942. if (empty($info['release_state'])) {
  943. $errors[] = 'missing release state';
  944. } elseif (!in_array($info['release_state'], $_PEAR_Common_release_states)) {
  945. $errors[] = "invalid release state `$info[release_state]', should be one of: ".implode(' ', $_PEAR_Common_release_states);
  946. }
  947. if (empty($info['release_date'])) {
  948. $errors[] = 'missing release date';
  949. } elseif (!preg_match('/^\d{4}-\d\d-\d\d$/', $info['release_date'])) {
  950. $errors[] = "invalid release date `$info[release_date]', format is YYYY-MM-DD";
  951. }
  952. if (empty($info['release_notes'])) {
  953. $errors[] = "missing release notes";
  954. }
  955. if (empty($info['maintainers'])) {
  956. $errors[] = 'no maintainer(s)';
  957. } else {
  958. $i = 1;
  959. foreach ($info['maintainers'] as $m) {
  960. if (empty($m['handle'])) {
  961. $errors[] = "maintainer $i: missing handle";
  962. }
  963. if (empty($m['role'])) {
  964. $errors[] = "maintainer $i: missing role";
  965. } elseif (!in_array($m['role'], $_PEAR_Common_maintainer_roles)) {
  966. $errors[] = "maintainer $i: invalid role `$m[role]', should be one of: ".implode(' ', $_PEAR_Common_maintainer_roles);
  967. }
  968. if (empty($m['name'])) {
  969. $errors[] = "maintainer $i: missing name";
  970. }
  971. if (empty($m['email'])) {
  972. $errors[] = "maintainer $i: missing email";
  973. }
  974. $i++;
  975. }
  976. }
  977. if (!empty($info['deps'])) {
  978. $i = 1;
  979. foreach ($info['deps'] as $d) {
  980. if (empty($d['type'])) {
  981. $errors[] = "depenency $i: missing type";
  982. } elseif (!in_array($d['type'], $_PEAR_Common_dependency_types)) {
  983. $errors[] = "dependency $i: invalid type, should be one of: ".implode(' ', $_PEAR_Common_depenency_types);
  984. }
  985. if (empty($d['rel'])) {
  986. $errors[] = "dependency $i: missing relation";
  987. } elseif (!in_array($d['rel'], $_PEAR_Common_dependency_relations)) {
  988. $errors[] = "dependency $i: invalid relation, should be one of: ".implode(' ', $_PEAR_Common_dependency_relations);
  989. }
  990. if ($d['rel'] != 'has' && empty($d['version'])) {
  991. $warnings[] = "dependency $i: missing version";
  992. } elseif ($d['rel'] == 'has' && !empty($d['version'])) {
  993. $warnings[] = "dependency $i: version ignored for `has' dependencies";
  994. }
  995. if ($d['type'] == 'php' && !empty($d['name'])) {
  996. $warnings[] = "dependency $i: name ignored for php type dependencies";
  997. } elseif ($d['type'] != 'php' && empty($d['name'])) {
  998. $errors[] = "dependency $i: missing name";
  999. }
  1000. $i++;
  1001. }
  1002. }
  1003. if (!empty($info['configure_options'])) {
  1004. $i = 1;
  1005. foreach ($info['configure_options'] as $c) {
  1006. if (empty($c['name'])) {
  1007. $errors[] = "configure option $i: missing name";
  1008. }
  1009. if (empty($c['prompt'])) {
  1010. $errors[] = "configure option $i: missing prompt";
  1011. }
  1012. }
  1013. }
  1014. if (empty($info['filelist'])) {
  1015. $errors[] = 'no files';
  1016. } else {
  1017. foreach ($info['filelist'] as $file => $fa) {
  1018. if (empty($fa['role'])) {
  1019. $errors[] = "file $file: missing role";
  1020. } elseif (!in_array($fa['role'], $_PEAR_Common_file_roles)) {
  1021. $errors[] = "file $file: invalid role, should be one of: ".implode(' ', $_PEAR_Common_file_roles);
  1022. }
  1023. // (ssb) Any checks we can do for baseinstalldir?
  1024. // (cox) Perhaps checks that either the target dir and
  1025. // baseInstall doesn't cointain "../../"
  1026. }
  1027. }
  1028. return true;
  1029. }
  1030. // }}}
  1031. // {{{ analyzeSourceCode()
  1032. /**
  1033. * Analyze the source code of the given PHP file
  1034. *
  1035. * @param string Filename of the PHP file
  1036. * @return mixed
  1037. * @access public
  1038. */
  1039. function analyzeSourceCode($file)
  1040. {
  1041. if (!function_exists("token_get_all")) {
  1042. return false;
  1043. }
  1044. if (!$fp = @fopen($file, "r")) {
  1045. return false;
  1046. }
  1047. $contents = fread($fp, filesize($file));
  1048. $tokens = token_get_all($contents);
  1049. /*
  1050. for ($i = 0; $i < sizeof($tokens); $i++) {
  1051. list($token, $data) = $tokens[$i];
  1052. if (is_string($token)) {
  1053. var_dump($token);
  1054. } else {
  1055. print token_name($token) . ' ';
  1056. var_dump(rtrim($data));
  1057. }
  1058. }
  1059. */
  1060. $look_for = 0;
  1061. $paren_level = 0;
  1062. $bracket_level = 0;
  1063. $brace_level = 0;
  1064. $lastphpdoc = '';
  1065. $current_class = '';
  1066. $current_class_level = -1;
  1067. $current_function = '';
  1068. $current_function_level = -1;
  1069. $declared_classes = array();
  1070. $declared_functions = array();
  1071. $declared_methods = array();
  1072. $used_classes = array();
  1073. $used_functions = array();
  1074. $nodeps = array();
  1075. for ($i = 0; $i < sizeof($tokens); $i++) {
  1076. list($token, $data) = $tokens[$i];
  1077. switch ($token) {
  1078. case '{': $brace_level++; continue 2;
  1079. case '}':
  1080. $brace_level--;
  1081. if ($current_class_level == $brace_level) {
  1082. $current_class = '';
  1083. $current_class_level = -1;
  1084. }
  1085. if ($current_function_level == $brace_level) {
  1086. $current_function = '';
  1087. $current_function_level = -1;
  1088. }
  1089. continue 2;
  1090. case '[': $bracket_level++; continue 2;
  1091. case ']': $bracket_level--; continue 2;
  1092. case '(': $paren_level++; continue 2;
  1093. case ')': $paren_level--; continue 2;
  1094. case T_CLASS:
  1095. case T_FUNCTION:
  1096. case T_NEW:
  1097. $look_for = $token;
  1098. continue 2;
  1099. case T_STRING:
  1100. if ($look_for == T_CLASS) {
  1101. $current_class = $data;
  1102. $current_class_level = $brace_level;
  1103. $declared_classes[] = $current_class;
  1104. } elseif ($look_for == T_FUNCTION) {
  1105. if ($current_class) {
  1106. $current_function = "$current_class::$data";
  1107. $declared_methods[$current_class][] = $data;
  1108. } else {
  1109. $current_function = $data;
  1110. $declared_functions[] = $current_function;
  1111. }
  1112. $current_function_level = $brace_level;
  1113. $m = array();
  1114. } elseif ($look_for == T_NEW) {
  1115. $used_classes[$data] = true;
  1116. }
  1117. $look_for = 0;
  1118. continue 2;
  1119. case T_VARIABLE:
  1120. $look_for = 0;
  1121. continue 2;
  1122. case T_COMMENT:
  1123. if (preg_match('!^/\*\*\s!', $data)) {
  1124. $lastphpdoc = $data;
  1125. if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
  1126. $nodeps = array_merge($nodeps, $m[1]);
  1127. }
  1128. }
  1129. continue 2;
  1130. case T_DOUBLE_COLON:
  1131. $class = $tokens[$i - 1][1];
  1132. if (strtolower($class) != 'parent') {
  1133. $used_classes[$class] = true;
  1134. }
  1135. continue 2;
  1136. }
  1137. }
  1138. return array(
  1139. "declared_classes" => $declared_classes,
  1140. "declared_methods" => $declared_methods,
  1141. "declared_functions" => $declared_functions,
  1142. "used_classes" => array_diff(array_keys($used_classes), $nodeps),
  1143. );
  1144. }
  1145. // }}}
  1146. // {{{ detectDependencies()
  1147. function detectDependencies($any, $status_callback = null)
  1148. {
  1149. if (!function_exists("token_get_all")) {
  1150. return false;
  1151. }
  1152. if (PEAR::isError($info = $this->infoFromAny($any))) {
  1153. return $this->raiseError($info);
  1154. }
  1155. if (!is_array($info)) {
  1156. return false;
  1157. }
  1158. $deps = array();
  1159. $used_c = $decl_c = $decl_f = $decl_m = array();
  1160. foreach ($info['filelist'] as $file => $fa) {
  1161. $tmp = $this->analyzeSourceCode($file);
  1162. $used_c = @array_merge($used_c, $tmp['used_classes']);
  1163. $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
  1164. $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
  1165. $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
  1166. }
  1167. $used_c = array_unique($used_c);
  1168. $decl_c = array_unique($decl_c);
  1169. $undecl_c = array_diff($used_c, $decl_c);
  1170. return array('used_classes' => $used_c,
  1171. 'declared_classes' => $decl_c,
  1172. 'declared_methods' => $decl_m,
  1173. 'declared_functions' => $decl_f,
  1174. 'undeclared_classes' => $undecl_c,
  1175. );
  1176. }
  1177. // }}}
  1178. // {{{ getUserRoles()
  1179. /**
  1180. * Get the valid roles for a PEAR package maintainer
  1181. *
  1182. * @return array
  1183. * @static
  1184. */
  1185. function getUserRoles()
  1186. {
  1187. return $GLOBALS['_PEAR_Common_maintainer_roles'];
  1188. }
  1189. // }}}
  1190. // {{{ getReleaseStates()
  1191. /**
  1192. * Get the valid package release states of packages
  1193. *
  1194. * @return array
  1195. * @static
  1196. */
  1197. function getReleaseStates()
  1198. {
  1199. return $GLOBALS['_PEAR_Common_release_states'];
  1200. }
  1201. // }}}
  1202. // {{{ getDependencyTypes()
  1203. /**
  1204. * Get the implemented dependency types (php, ext, pkg etc.)
  1205. *
  1206. * @return array
  1207. * @static
  1208. */
  1209. function getDependencyTypes()
  1210. {
  1211. return $GLOBALS['_PEAR_Common_dependency_types'];
  1212. }
  1213. // }}}
  1214. // {{{ getDependencyRelations()
  1215. /**
  1216. * Get the implemented dependency relations (has, lt, ge etc.)
  1217. *
  1218. * @return array
  1219. * @static
  1220. */
  1221. function getDependencyRelations()
  1222. {
  1223. return $GLOBALS['_PEAR_Common_dependency_relations'];
  1224. }
  1225. // }}}
  1226. // {{{ getFileRoles()
  1227. /**
  1228. * Get the implemented file roles
  1229. *
  1230. * @return array
  1231. * @static
  1232. */
  1233. function getFileRoles()
  1234. {
  1235. return $GLOBALS['_PEAR_Common_file_roles'];
  1236. }
  1237. // }}}
  1238. // {{{ getReplacementTypes()
  1239. /**
  1240. * Get the implemented file replacement types in
  1241. *
  1242. * @return array
  1243. * @static
  1244. */
  1245. function getReplacementTypes()
  1246. {
  1247. return $GLOBALS['_PEAR_Common_replacement_types'];
  1248. }
  1249. // }}}
  1250. // {{{ getProvideTypes()
  1251. /**
  1252. * Get the implemented file replacement types in
  1253. *
  1254. * @return array
  1255. * @static
  1256. */
  1257. function getProvideTypes()
  1258. {
  1259. return $GLOBALS['_PEAR_Common_provide_types'];
  1260. }
  1261. // }}}
  1262. // {{{ getScriptPhases()
  1263. /**
  1264. * Get the implemented file replacement types in
  1265. *
  1266. * @return array
  1267. * @static
  1268. */
  1269. function getScriptPhases()
  1270. {
  1271. return $GLOBALS['_PEAR_Common_script_phases'];
  1272. }
  1273. // }}}
  1274. // {{{ validPackageName()
  1275. /**
  1276. * Test whether a string contains a valid package name.
  1277. *
  1278. * @param string $name the package name to test
  1279. *
  1280. * @return bool
  1281. *
  1282. * @access public
  1283. */
  1284. function validPackageName($name)
  1285. {
  1286. return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
  1287. }
  1288. // }}}
  1289. // {{{ downloadHttp()
  1290. /**
  1291. * Download a file through HTTP. Considers suggested file name in
  1292. * Content-disposition: header and can run a callback function for
  1293. * different events. The callback will be called with two
  1294. * parameters: the callback type, and parameters. The implemented
  1295. * callback types are:
  1296. *
  1297. * 'setup' called at the very beginning, parameter is a UI object
  1298. * that should be used for all output
  1299. * 'message' the parameter is a string with an informational message
  1300. * 'saveas' may be used to save with a different file name, the
  1301. * parameter is the filename that is about to be used.
  1302. * If a 'saveas' callback returns a non-empty string,
  1303. * that file name will be used as the filename instead.
  1304. * Note that $save_dir will not be affected by this, only
  1305. * the basename of the file.
  1306. * 'start' download is starting, parameter is number of bytes
  1307. * that are expected, or -1 if unknown
  1308. * 'bytesread' parameter is the number of bytes read so far
  1309. * 'done' download is complete, parameter is the total number
  1310. * of bytes read
  1311. * 'connfailed' if the TCP connection fails, this callback is called
  1312. * with array(host,port,errno,errmsg)
  1313. * 'writefailed' if writing to disk fails, this callback is called
  1314. * with array(destfile,errmsg)
  1315. *
  1316. * If an HTTP proxy has been configured (http_proxy PEAR_Config
  1317. * setting), the proxy will be used.
  1318. *
  1319. * @param string $url the URL to download
  1320. * @param object $ui PEAR_Frontend_* instance
  1321. * @param object $config PEAR_Config instance
  1322. * @param string $save_dir (optional) directory to save file in
  1323. * @param mixed $callback (optional) function/method to call for status
  1324. * updates
  1325. *
  1326. * @return string Returns the full path of the downloaded file or a PEAR
  1327. * error on failure. If the error is caused by
  1328. * socket-related errors, the error object will
  1329. * have the fsockopen error code available through
  1330. * getCode().
  1331. *
  1332. * @access public
  1333. */
  1334. function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
  1335. {
  1336. if ($callback) {
  1337. call_user_func($callback, 'setup', array(&$ui));
  1338. }
  1339. if (preg_match('!^http://([^/:?#]*)(:(\d+))?(/.*)!', $url, $matches)) {
  1340. list(,$host,,$port,$path) = $matches;
  1341. }
  1342. if (isset($this)) {
  1343. $config = &$this->config;
  1344. } else {
  1345. $config = &PEAR_Config::singleton();
  1346. }
  1347. $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
  1348. if ($proxy = parse_url($config->get('http_proxy'))) {
  1349. $proxy_host = @$proxy['host'];
  1350. $proxy_port = @$proxy['port'];
  1351. $proxy_user = @$proxy['user'];
  1352. $proxy_pass = @$proxy['pass'];
  1353. if ($proxy_port == '') {
  1354. $proxy_port = 8080;
  1355. }
  1356. if ($callback) {
  1357. call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
  1358. }
  1359. }
  1360. if (empty($port)) {
  1361. $port = 80;
  1362. }
  1363. if ($proxy_host != '') {
  1364. $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
  1365. if (!$fp) {
  1366. if ($callback) {
  1367. call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
  1368. $errno, $errstr));
  1369. }
  1370. return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
  1371. }
  1372. $request = "GET $url HTTP/1.0\r\n";
  1373. } else {
  1374. $fp = @fsockopen($host, $port, $errno, $errstr);
  1375. if (!$fp) {
  1376. if ($callback) {
  1377. call_user_func($callback, 'connfailed', array($host, $port,
  1378. $errno, $errstr));
  1379. }
  1380. return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
  1381. }
  1382. $request = "GET $path HTTP/1.0\r\n";
  1383. }
  1384. $request .= "Host: $host:$port\r\n".
  1385. "User-Agent: PHP/".PHP_VERSION."\r\n";
  1386. if ($proxy_host != '' && $proxy_user != '') {
  1387. $request .= 'Proxy-Authorization: Basic ' .
  1388. base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
  1389. }
  1390. $request .= "\r\n";
  1391. fwrite($fp, $request);
  1392. $headers = array();
  1393. while (trim($line = fgets($fp, 1024))) {
  1394. if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) {

Large files files are truncated, but you can click here to view the full file