PageRenderTime 80ms CodeModel.GetById 32ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/lux/sitellite
PHP | 1552 lines | 1163 code | 68 blank | 321 comment | 102 complexity | 6fab59496c247cefd9956ab34d7a9dea MD5 | raw file

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

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